pipeline {
  agent { label 'py-kaniko' } // JCasC 파드 템플릿: jnlp / ci-python / tooling / kaniko

  options { timestamps() }

  parameters {
    string(name: 'BRANCH_NAME',     defaultValue: '', description: '빌드할 브랜치명 (비우면 main)')
    string(name: 'GIT_COMMIT_HASH', defaultValue: '', description: '빌드할 커밋 해시 (비우면 최신 커밋)')
  }

  environment {
    NODE_ID           = '58954960-0175-4111-a4e3-44d79aeed72a'
    NODE_TYPE         = 'agent'
    NAMESPACE         = 'maxis-dev'
    REGISTRY          = 'maxis.azurecr.io'
    PLATFORM_NAMESPACE = 'maxis-dev'
    IMAGE_PULL_SECRET = 'cr-pull'
  }

  stages {

    // ──────────────────────────────────────────────
    // Stage 1. Checkout
    // - 브랜치 / 커밋 해시 기준으로 소스코드 체크아웃
    // - 이미지 경로 / release 이름 등 이후 stage 에서 사용할 환경변수 세팅
    // ──────────────────────────────────────────────
    stage('Checkout') {
      steps {
        container('jnlp') {
          script {
            sh 'git fetch --all || true'

            def checkoutBranch = params.BRANCH_NAME?.trim() ?: 'main'
            def commitHash     = params.GIT_COMMIT_HASH?.trim()

            if (commitHash) {
              sh "git fetch origin ${checkoutBranch}"
              sh "git checkout ${checkoutBranch}"
              sh "git checkout ${commitHash}"
            } else {
              sh "git fetch origin ${checkoutBranch}"
              sh "git checkout ${checkoutBranch}"
              commitHash = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
            }

            env.BRANCH_NAME     = checkoutBranch
            env.GIT_COMMIT_HASH = commitHash
            env.RELEASE_NAME    = "node-${NODE_ID}-${checkoutBranch}"
            env.IMAGE_REPO      = "${REGISTRY}/node.${NODE_ID}.${checkoutBranch}"
            env.IMAGE_TAG       = commitHash
            env.FULL_IMAGE      = "${env.IMAGE_REPO}:${env.IMAGE_TAG}"

            echo "NODE_ID=${NODE_ID}"
            echo "BRANCH_NAME=${env.BRANCH_NAME}"
            echo "GIT_COMMIT_HASH=${env.GIT_COMMIT_HASH}"
            echo "RELEASE_NAME=${env.RELEASE_NAME}"
            echo "FULL_IMAGE=${env.FULL_IMAGE}"
            echo "IMAGE_PULL_SECRET=${IMAGE_PULL_SECRET}"
          }
        }
      }
    }

    // ──────────────────────────────────────────────
    // Stage 2. Build
    // - Kaniko 로 Docker 이미지 빌드 후 레지스트리에 푸시
    // - 실패 시 로그에 pip install / SyntaxError 등 원인 출력
    // - retry(3): 레지스트리 일시적 네트워크 오류 대비
    // ──────────────────────────────────────────────
    stage('Build') {
      steps {
        container('kaniko') {
          retry(3) {
            sh '''
              set -e
              /kaniko/executor \
                --context=$WORKSPACE \
                --dockerfile=Dockerfile \
                --destination=$FULL_IMAGE \
                --cache=true \
                --snapshot-mode=time
            '''
          }
        }
      }
      post {
        failure {
          echo '[Build] ★ BUILD FAILED ★'
          echo '[Build] Kaniko 빌드 실패. 위 로그에서 원인을 확인하세요.'
          echo '[Build] 주요 원인: Dockerfile 오류 / pip install 실패 / SyntaxError / ModuleNotFoundError'
        }
      }
    }

    // ──────────────────────────────────────────────
    // Stage 3. Deploy
    // - K8s manifest 렌더링 후 kubectl apply
    // - imagePullSecret 없으면 platform 네임스페이스에서 복제
    // - rollout status 로 배포 완료 대기 (최대 10분)
    // - spec 변경 없으면 rollout restart 실행
    // ──────────────────────────────────────────────
    stage('Deploy') {
      steps {
        container('tooling') {
          sh '''
            set -euo pipefail

            # imagePullSecret 복제 (platform → NAMESPACE)
            if [ -n "${IMAGE_PULL_SECRET}" ] && ! kubectl -n "${NAMESPACE}" get secret "${IMAGE_PULL_SECRET}" >/dev/null 2>&1; then
              echo "[Deploy] imagePullSecret '${IMAGE_PULL_SECRET}' 복제 중 (platform → ${NAMESPACE})"
              
              kubectl -n "${PLATFORM_NAMESPACE}" get secret "${IMAGE_PULL_SECRET}" -o go-template='{{ index .data ".dockerconfigjson" }}' > .dockercfg.b64
              
              base64 -d .dockercfg.b64 > .dockerconfigjson
              kubectl -n "${NAMESPACE}" create secret generic "${IMAGE_PULL_SECRET}" \
                --type=kubernetes.io/dockerconfigjson \
                --from-file=.dockerconfigjson
              rm -f .dockercfg.b64 .dockerconfigjson
            fi

            # 배포 전 현재 generation 저장 (spec 변경 감지용)
            PREV_GEN=""
            if kubectl -n "${NAMESPACE}" get deploy "${RELEASE_NAME}" >/dev/null 2>&1; then
              PREV_GEN="$(kubectl -n "${NAMESPACE}" get deploy "${RELEASE_NAME}" -o jsonpath='{.metadata.generation}' 2>/dev/null || true)"
            fi
            echo "[Deploy] PREV_GEN=${PREV_GEN:-<none>}"

            # manifest 렌더링 (envsubst 로 배포마다 바뀌는 값 주입)
            rm -rf .rendered && mkdir -p .rendered
            export RELEASE_NAME FULL_IMAGE BRANCH_NAME
            for f in k8s/*.yaml; do
              echo "[Deploy] rendering $f"
              envsubst '${RELEASE_NAME} ${FULL_IMAGE} ${BRANCH_NAME}' < "$f" > ".rendered/$(basename "$f")"
            done

            echo "[Deploy] --- Rendered files ---"
            ls -al .rendered

            kubectl -n "${NAMESPACE}" apply -f .rendered/

            # rollout 완료 대기 (최대 10분)
            echo "[Deploy] rollout status 대기 중..."
            kubectl -n "${NAMESPACE}" rollout status deploy "${RELEASE_NAME}" --timeout=10m

            # spec 변경 없으면 rollout restart
            POST_GEN="$(kubectl -n "${NAMESPACE}" get deploy "${RELEASE_NAME}" -o jsonpath='{.metadata.generation}' 2>/dev/null || true)"
            echo "[Deploy] POST_GEN=${POST_GEN:-<none>}"

            if [ -n "${PREV_GEN}" ] && [ -n "${POST_GEN}" ] && [ "${PREV_GEN}" = "${POST_GEN}" ]; then
              echo "[Deploy] Spec 변경 없음 (generation=${POST_GEN}). rollout restart 실행"
              kubectl -n "${NAMESPACE}" rollout restart deploy "${RELEASE_NAME}"
              kubectl -n "${NAMESPACE}" rollout status  deploy "${RELEASE_NAME}" --timeout=10m
            else
              echo "[Deploy] Spec 변경 감지 또는 최초 배포. rollout restart skip"
            fi

            echo "[Deploy] --- 배포 후 리소스 상태 ---"
            kubectl -n "${NAMESPACE}" get deploy,po,svc \
              -l watcher.nodeId="${NODE_ID}",watcher.role="${NODE_TYPE}" || true
          '''
        }
      }
      post {
        failure {
          container('tooling') {
            echo '[Deploy] ★ DEPLOY FAILED ★'
            sh '''
              echo "[Deploy] --- Deployment 상태 ---"
              kubectl -n "${NAMESPACE}" describe deploy "${RELEASE_NAME}" || true
              echo "[Deploy] --- Pod 이벤트 ---"
              kubectl -n "${NAMESPACE}" get events \
                --field-selector involvedObject.name="${RELEASE_NAME}" \
                --sort-by='.lastTimestamp' || true
            '''
          }
        }
      }
    }

    // ──────────────────────────────────────────────
    // Stage 4. Verify
    // - rollout 완료 후 Pod 실제 정상 여부 최종 검증
    // - Ready condition + restartCount 확인
    // - CrashLoopBackOff 즉시 감지
    // - 성공 시: Pod 기동 로그 첫 30줄 출력
    // - 실패 시: Pod 로그 마지막 50줄 + describe 출력 → Poller 가 수집
    // ──────────────────────────────────────────────
    stage('Verify') {
      steps {
        container('tooling') {
          sh '''
            set -euo pipefail

            MAX_WAIT=120
            INTERVAL=5
            ELAPSED=0

            echo "[Verify] Pod 상태 확인 시작 (최대 ${MAX_WAIT}s)"

            while [ $ELAPSED -lt $MAX_WAIT ]; do

              POD_NAME="$(kubectl -n "${NAMESPACE}" get po \
                -l app.kubernetes.io/name="${RELEASE_NAME}" \
                --sort-by=.metadata.creationTimestamp \
                -o jsonpath='{.items[-1].metadata.name}' 2>/dev/null || true)"

              if [ -z "$POD_NAME" ]; then
                echo "[Verify] elapsed=${ELAPSED}s | Pod 아직 없음, 대기 중..."
                sleep $INTERVAL
                ELAPSED=$((ELAPSED + INTERVAL))
                continue
              fi

              PHASE="$(kubectl -n "${NAMESPACE}" get po "${POD_NAME}" \
                -o jsonpath='{.status.phase}' 2>/dev/null || true)"

              READY="$(kubectl -n "${NAMESPACE}" get po "${POD_NAME}" \
                -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || true)"

              RESTART_COUNT="$(kubectl -n "${NAMESPACE}" get po "${POD_NAME}" \
                -o jsonpath='{.status.containerStatuses[0].restartCount}' 2>/dev/null || echo '0')"

              WAITING_REASON="$(kubectl -n "${NAMESPACE}" get po "${POD_NAME}" \
                -o jsonpath='{.status.containerStatuses[0].state.waiting.reason}' 2>/dev/null || true)"

              echo "[Verify] elapsed=${ELAPSED}s | Pod=${POD_NAME} | Phase=${PHASE} | Ready=${READY} | Restarts=${RESTART_COUNT} | WaitReason=${WAITING_REASON}"

              # CrashLoopBackOff 즉시 실패 처리
              if [ "${WAITING_REASON}" = "CrashLoopBackOff" ]; then
                echo "[Verify] ★ VERIFY FAILED ★ CrashLoopBackOff 감지"
                echo "[Verify] --- Pod 로그 (마지막 50줄) ---"
                kubectl -n "${NAMESPACE}" logs "${POD_NAME}" --tail=50 || true
                echo "[Verify] --- 이전 컨테이너 로그 ---"
                kubectl -n "${NAMESPACE}" logs "${POD_NAME}" --previous --tail=50 || true
                echo "[Verify] --- Pod describe ---"
                kubectl -n "${NAMESPACE}" describe po "${POD_NAME}" || true
                exit 1
              fi

              # Phase Failed 즉시 실패 처리
              if [ "${PHASE}" = "Failed" ]; then
                echo "[Verify] ★ VERIFY FAILED ★ Pod Phase=Failed"
                echo "[Verify] --- Pod 로그 (마지막 50줄) ---"
                kubectl -n "${NAMESPACE}" logs "${POD_NAME}" --tail=50 || true
                echo "[Verify] --- Pod describe ---"
                kubectl -n "${NAMESPACE}" describe po "${POD_NAME}" || true
                exit 1
              fi

              # restartCount > 0 이면 불안정 판정
              if [ "${RESTART_COUNT}" -gt 0 ]; then
                echo "[Verify] ★ VERIFY FAILED ★ 재시작 감지 (restartCount=${RESTART_COUNT})"
                echo "[Verify] --- Pod 로그 (마지막 50줄) ---"
                kubectl -n "${NAMESPACE}" logs "${POD_NAME}" --tail=50 || true
                echo "[Verify] --- 이전 컨테이너 로그 ---"
                kubectl -n "${NAMESPACE}" logs "${POD_NAME}" --previous --tail=50 || true
                echo "[Verify] --- Pod describe ---"
                kubectl -n "${NAMESPACE}" describe po "${POD_NAME}" || true
                exit 1
              fi

              # Ready=True 확인 → 최종 성공
              if [ "${READY}" = "True" ]; then
                echo "[Verify] ✅ VERIFY SUCCESS"
                echo "[Verify] Pod=${POD_NAME} | Phase=${PHASE} | Ready=${READY} | Restarts=${RESTART_COUNT}"
                echo "[Verify] --- Pod 기동 로그 (첫 30줄) ---"
                kubectl -n "${NAMESPACE}" logs "${POD_NAME}" | head -30 || true
                echo "[Verify] --- 최종 리소스 상태 ---"
                kubectl -n "${NAMESPACE}" get deploy,po,svc \
                  -l watcher.nodeId="${NODE_ID}",watcher.role="${NODE_TYPE}" || true
                exit 0
              fi

              sleep $INTERVAL
              ELAPSED=$((ELAPSED + INTERVAL))
            done

            # 타임아웃
            echo "[Verify] ★ VERIFY FAILED ★ 타임아웃 (${MAX_WAIT}s 초과)"
            echo "[Verify] --- Pod 로그 (마지막 50줄) ---"
            kubectl -n "${NAMESPACE}" logs "${POD_NAME}" --tail=50 || true
            echo "[Verify] --- Pod describe ---"
            kubectl -n "${NAMESPACE}" describe po "${POD_NAME}" || true
            exit 1
          '''
        }
      }
      post {
        failure {
          container('tooling') {
            echo '[Verify] ★ VERIFY FAILED - post ★'
            sh '''
              echo "[Verify] --- Deployment 최종 상태 ---"
              kubectl -n "${NAMESPACE}" get deploy "${RELEASE_NAME}" -o wide || true
              echo "[Verify] --- Pod 최종 상태 ---"
              kubectl -n "${NAMESPACE}" get po \
                -l app.kubernetes.io/name="${RELEASE_NAME}" -o wide || true
            '''
          }
        }
      }
    }
  }

  post {
    success {
      echo '✅ DEPLOY SUCCESS'
      echo "NODE_ID=${NODE_ID} | BRANCH=${env.BRANCH_NAME} | COMMIT=${env.GIT_COMMIT_HASH}"
    }
    failure {
      echo '★ DEPLOY FAILED ★'
      echo "NODE_ID=${NODE_ID} | BRANCH=${env.BRANCH_NAME} | COMMIT=${env.GIT_COMMIT_HASH}"
    }
    always {
      container('tooling') {
        sh '''
          echo "--- FINAL SUMMARY ---"
          kubectl -n "${NAMESPACE}" get deploy "${RELEASE_NAME}" -o wide || true
          kubectl -n "${NAMESPACE}" get po \
            -l app.kubernetes.io/name="${RELEASE_NAME}" -o wide || true
          kubectl -n "${NAMESPACE}" get svc "${RELEASE_NAME}" -o wide || true
        '''
      }
      cleanWs()
    }
  }
}