#!/bin/bash
# generate-docker-steps.sh - Generate Woodpecker CI pipeline steps for Docker build/push/link
#
# Outputs valid Woodpecker YAML for:
#   - Kaniko Docker build & push steps (one per service)
#   - Gitea package linking step
#   - npm package publish step (optional)
#
# Usage:
#   generate-docker-steps.sh \
#     --registry git.uscllc.com \
#     --org usc \
#     --repo uconnect \
#     --service backend-api:src/backend-api/Dockerfile \
#     --service web-portal:src/web-portal/Dockerfile \
#     --branches main,develop \
#     [--build-arg backend-api:NEXT_PUBLIC_API_URL=https://api.example.com] \
#     [--npm-package @uconnect/schemas:src/schemas] \
#     [--npm-registry https://git.uscllc.com/api/packages/usc/npm/] \
#     [--depends-on build]

set -e

# Defaults
REGISTRY=""
ORG=""
REPO=""
BRANCHES="main,develop"
DEPENDS_ON="build"
declare -a SERVICES=()
declare -a BUILD_ARGS=()
declare -a NPM_PACKAGES=()
NPM_REGISTRY=""

show_help() {
    cat <<'EOF'
Usage: generate-docker-steps.sh [OPTIONS]

Generate Woodpecker CI YAML for Docker build/push/link via Kaniko.

Required:
  --registry <host>              Gitea hostname (e.g., git.uscllc.com)
  --org <name>                   Gitea organization (e.g., usc)
  --repo <name>                  Repository name (e.g., uconnect)
  --service <name:dockerfile>    Service to build (repeatable)

Optional:
  --branches <list>              Comma-separated branches (default: main,develop)
  --depends-on <step>            Step name Docker builds depend on (default: build)
  --build-arg <service:KEY=VAL>  Build arg for a service (repeatable)
  --npm-package <pkg:path>       npm package to publish (repeatable)
  --npm-registry <url>           npm registry URL for publishing
  --kaniko-setup-only            Output just the kaniko_setup YAML anchor
  -h, --help                     Show this help

Examples:
  # Mosaic Stack pattern
  generate-docker-steps.sh \
    --registry git.mosaicstack.dev --org mosaic --repo stack \
    --service stack-api:apps/api/Dockerfile \
    --service stack-web:apps/web/Dockerfile \
    --build-arg stack-web:NEXT_PUBLIC_API_URL=https://api.mosaicstack.dev

  # U-Connect pattern
  generate-docker-steps.sh \
    --registry git.uscllc.com --org usc --repo uconnect \
    --service uconnect-backend-api:src/backend-api/Dockerfile \
    --service uconnect-web-portal:src/web-portal/Dockerfile \
    --service uconnect-ingest-api:src/ingest-api/Dockerfile \
    --branches main,develop
EOF
    exit 0
}

KANIKO_SETUP_ONLY=false

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        --registry) REGISTRY="$2"; shift 2 ;;
        --org) ORG="$2"; shift 2 ;;
        --repo) REPO="$2"; shift 2 ;;
        --service) SERVICES+=("$2"); shift 2 ;;
        --branches) BRANCHES="$2"; shift 2 ;;
        --depends-on) DEPENDS_ON="$2"; shift 2 ;;
        --build-arg) BUILD_ARGS+=("$2"); shift 2 ;;
        --npm-package) NPM_PACKAGES+=("$2"); shift 2 ;;
        --npm-registry) NPM_REGISTRY="$2"; shift 2 ;;
        --kaniko-setup-only) KANIKO_SETUP_ONLY=true; shift ;;
        -h|--help) show_help ;;
        *) echo "Unknown option: $1" >&2; exit 1 ;;
    esac
done

# Validate required args
if [[ -z "$REGISTRY" ]]; then echo "Error: --registry is required" >&2; exit 1; fi
if [[ -z "$ORG" ]]; then echo "Error: --org is required" >&2; exit 1; fi
if [[ -z "$REPO" ]]; then echo "Error: --repo is required" >&2; exit 1; fi
if [[ ${#SERVICES[@]} -eq 0 && "$KANIKO_SETUP_ONLY" != true ]]; then
    echo "Error: at least one --service is required" >&2; exit 1
fi

# Parse branches into YAML list
IFS=',' read -ra BRANCH_LIST <<< "$BRANCHES"
BRANCH_YAML="["
for i in "${!BRANCH_LIST[@]}"; do
    if [[ $i -gt 0 ]]; then BRANCH_YAML="$BRANCH_YAML, "; fi
    BRANCH_YAML="$BRANCH_YAML${BRANCH_LIST[$i]}"
done
BRANCH_YAML="$BRANCH_YAML]"

# Helper: get build args for a specific service
get_build_args_for_service() {
    local svc_name="$1"
    local args=()
    for ba in "${BUILD_ARGS[@]}"; do
        local ba_svc="${ba%%:*}"
        local ba_val="${ba#*:}"
        if [[ "$ba_svc" == "$svc_name" ]]; then
            args+=("$ba_val")
        fi
    done
    echo "${args[@]}"
}

# Helper: determine Dockerfile context from path
# e.g., apps/api/Dockerfile -> . (monorepo root)
#        docker/postgres/Dockerfile -> docker/postgres
get_context() {
    local dockerfile="$1"
    local dir
    dir=$(dirname "$dockerfile")
    # If Dockerfile is at project root or in a top-level apps/src dir, use "."
    if [[ "$dir" == "." || "$dir" == apps/* || "$dir" == src/* || "$dir" == packages/* ]]; then
        echo "."
    else
        echo "$dir"
    fi
}

# ============================================================
# Output: YAML anchor for kaniko setup
# ============================================================
emit_kaniko_anchor() {
    cat <<EOF
  # Kaniko base command setup
  - &kaniko_setup |
    mkdir -p /kaniko/.docker
    echo "{\\"auths\\":{\\"${REGISTRY}\\":{\\"username\\":\\"\$GITEA_USER\\",\\"password\\":\\"\$GITEA_TOKEN\\"}}}" > /kaniko/.docker/config.json
EOF
}

if [[ "$KANIKO_SETUP_ONLY" == true ]]; then
    emit_kaniko_anchor
    exit 0
fi

# ============================================================
# Output: Header comment
# ============================================================
cat <<EOF

  # ======================
  # Docker Build & Push (${BRANCHES} only)
  # ======================
  # Generated by: generate-docker-steps.sh
  # Registry: ${REGISTRY}/${ORG}
  # Requires secrets: gitea_username, gitea_token
  #
  # Tagging Strategy:
  #   - Always: commit SHA (first 8 chars)
EOF

for b in "${BRANCH_LIST[@]}"; do
    case "$b" in
        main) echo "  #   - main branch: 'latest'" ;;
        develop) echo "  #   - develop branch: 'dev'" ;;
        *) echo "  #   - ${b} branch: '${b}'" ;;
    esac
done

echo "  #   - git tags: version tag (e.g., v1.0.0)"
echo ""

# ============================================================
# Output: Kaniko build step for each service
# ============================================================
for svc in "${SERVICES[@]}"; do
    SVC_NAME="${svc%%:*}"
    DOCKERFILE="${svc#*:}"

    CONTEXT=$(get_context "$DOCKERFILE")
    SVC_BUILD_ARGS=$(get_build_args_for_service "$SVC_NAME")

    # Build the kaniko command with build args
    KANIKO_EXTRA=""
    if [[ -n "$SVC_BUILD_ARGS" ]]; then
        for arg in $SVC_BUILD_ARGS; do
            KANIKO_EXTRA="$KANIKO_EXTRA --build-arg ${arg}"
        done
    fi

    cat <<EOF
  # Build and push ${SVC_NAME}
  docker-build-${SVC_NAME}:
    image: gcr.io/kaniko-project/executor:debug
    environment:
      GITEA_USER:
        from_secret: gitea_username
      GITEA_TOKEN:
        from_secret: gitea_token
      CI_COMMIT_BRANCH: \${CI_COMMIT_BRANCH}
      CI_COMMIT_TAG: \${CI_COMMIT_TAG}
      CI_COMMIT_SHA: \${CI_COMMIT_SHA}
    commands:
      - *kaniko_setup
      - |
        DESTINATIONS="--destination ${REGISTRY}/${ORG}/${SVC_NAME}:\${CI_COMMIT_SHA:0:8}"
EOF

    # Branch-specific tags
    for b in "${BRANCH_LIST[@]}"; do
        case "$b" in
            main)
                cat <<EOF
        if [ "\$CI_COMMIT_BRANCH" = "main" ]; then
          DESTINATIONS="\$DESTINATIONS --destination ${REGISTRY}/${ORG}/${SVC_NAME}:latest"
        fi
EOF
                ;;
            develop)
                cat <<EOF
        if [ "\$CI_COMMIT_BRANCH" = "develop" ]; then
          DESTINATIONS="\$DESTINATIONS --destination ${REGISTRY}/${ORG}/${SVC_NAME}:dev"
        fi
EOF
                ;;
            *)
                cat <<EOF
        if [ "\$CI_COMMIT_BRANCH" = "${b}" ]; then
          DESTINATIONS="\$DESTINATIONS --destination ${REGISTRY}/${ORG}/${SVC_NAME}:${b}"
        fi
EOF
                ;;
        esac
    done

    # Version tag
    cat <<EOF
        if [ -n "\$CI_COMMIT_TAG" ]; then
          DESTINATIONS="\$DESTINATIONS --destination ${REGISTRY}/${ORG}/${SVC_NAME}:\$CI_COMMIT_TAG"
        fi
        /kaniko/executor --context ${CONTEXT} --dockerfile ${DOCKERFILE}${KANIKO_EXTRA} \$DESTINATIONS
    when:
      - branch: ${BRANCH_YAML}
        event: [push, manual, tag]
    depends_on:
      - ${DEPENDS_ON}

EOF
done

# ============================================================
# Output: Package linking step
# ============================================================
cat <<EOF
  # ======================
  # Link Packages to Repository
  # ======================
  link-packages:
    image: alpine:3
    environment:
      GITEA_TOKEN:
        from_secret: gitea_token
    commands:
      - apk add --no-cache curl
      - echo "Waiting 10 seconds for packages to be indexed in registry..."
      - sleep 10
      - |
        set -e
        link_package() {
          PKG="\$\$1"
          echo "Linking \$\$PKG..."

          for attempt in 1 2 3; do
            STATUS=\$\$(curl -s -o /tmp/link-response.txt -w "%{http_code}" -X POST \\
              -H "Authorization: token \$\$GITEA_TOKEN" \\
              "https://${REGISTRY}/api/v1/packages/${ORG}/container/\$\$PKG/-/link/${REPO}")

            if [ "\$\$STATUS" = "201" ] || [ "\$\$STATUS" = "204" ]; then
              echo "  Linked \$\$PKG"
              return 0
            elif [ "\$\$STATUS" = "400" ]; then
              echo "  \$\$PKG already linked"
              return 0
            elif [ "\$\$STATUS" = "404" ] && [ \$\$attempt -lt 3 ]; then
              echo "  \$\$PKG not found yet, waiting 5s (attempt \$\$attempt/3)..."
              sleep 5
            else
              echo "  FAILED: \$\$PKG status \$\$STATUS"
              cat /tmp/link-response.txt
              return 1
            fi
          done
        }

EOF

# List all services to link
for svc in "${SERVICES[@]}"; do
    SVC_NAME="${svc%%:*}"
    echo "        link_package \"${SVC_NAME}\""
done

# Close the link step
cat <<EOF
    when:
      - branch: ${BRANCH_YAML}
        event: [push, manual, tag]
    depends_on:
EOF

for svc in "${SERVICES[@]}"; do
    SVC_NAME="${svc%%:*}"
    echo "      - docker-build-${SVC_NAME}"
done

echo ""

# ============================================================
# Output: npm publish step (if requested)
# ============================================================
if [[ ${#NPM_PACKAGES[@]} -gt 0 && -n "$NPM_REGISTRY" ]]; then
    cat <<EOF
  # ======================
  # Publish npm Packages
  # ======================
  publish-packages:
    image: node:20-alpine
    environment:
      GITEA_TOKEN:
        from_secret: gitea_token
    commands:
      - |
        echo "//${NPM_REGISTRY#https://}:_authToken=\$\$GITEA_TOKEN" > .npmrc
EOF

    # Detect scope from first package
    FIRST_PKG="${NPM_PACKAGES[0]}"
    PKG_NAME="${FIRST_PKG%%:*}"
    SCOPE="${PKG_NAME%%/*}"
    if [[ "$SCOPE" == @* ]]; then
        echo "        echo \"${SCOPE}:registry=${NPM_REGISTRY}\" >> .npmrc"
    fi

    for pkg in "${NPM_PACKAGES[@]}"; do
        PKG_NAME="${pkg%%:*}"
        PKG_PATH="${pkg#*:}"
        cat <<EOF
      - |
        CURRENT=\$\$(node -p "require('./${PKG_PATH}/package.json').version")
        PUBLISHED=\$\$(npm view ${PKG_NAME} version 2>/dev/null || echo "0.0.0")
        if [ "\$\$CURRENT" = "\$\$PUBLISHED" ]; then
          echo "${PKG_NAME}@\$\$CURRENT already published, skipping"
        else
          echo "Publishing ${PKG_NAME}@\$\$CURRENT (was \$\$PUBLISHED)"
          npm publish -w ${PKG_NAME}
        fi
EOF
    done

    cat <<EOF
    when:
      - branch: [main]
        event: [push, manual, tag]
    depends_on:
      - ${DEPENDS_ON}
EOF
fi
