/**
 * alien_py_cloud 统一部署流水线（dev / sit / uat / produ 共用）
 *
 * 设计原则：
 * - 无 parameters，Build Now 直接跑
 * - APP_ENV 由 Jenkins SCM 的 GIT_BRANCH 自动推断（分支 dev / sit / uat / produ）
 * - 端口、SSH 目标等环境差异全部来自 .env.${APP_ENV}，代码与 Jenkinsfile 四分支一致
 * - dev/sit/uat：Jenkins 本机 docker build + run
 * - produ：SSH 到 DEPLOY_SSH_TARGET 远程 build + run（目标见 .env.produ）
 */

def PROD_SSH_CREDENTIALS_ID = 'e611a045-2fdc-4613-babd-a72d69bf9814'

def readEnvVar(String envFile, String key, String defaultValue = '') {
  def val = sh(
    script: "grep -E '^${key}=' ${envFile} | head -n1 | cut -d= -f2- | tr -d '\"' | tr -d \"'\" | xargs || true",
    returnStdout: true
  ).trim()
  return val ?: defaultValue
}

pipeline {
  agent any

  options {
    buildDiscarder(logRotator(numToKeepStr: '10'))
    timestamps()
    disableConcurrentBuilds()
  }

  stages {

    stage('Resolve Environment') {
      steps {
        script {
          def raw = env.GIT_BRANCH ?: env.BRANCH_NAME ?: ''
          def branch = raw.replaceFirst('^origin/', '').replaceFirst('^refs/heads/', '').trim()

          if (!(branch in ['dev', 'sit', 'uat', 'produ'])) {
            error """
              ============ 无法识别当前部署环境 ============
              Jenkins 注入的 GIT_BRANCH = '${env.GIT_BRANCH}'
              解析后的分支名             = '${branch}'
              期望分支必须是 dev / sit / uat / produ 之一。

              请检查 Job 配置中的 Branch Specifier：
                DEV   → origin/dev 或 dev
                SIT   → origin/sit 或 sit
                UAT   → origin/uat 或 uat
                PRODU → origin/produ 或 produ
              =============================================
            """.stripIndent()
          }

          env.APP_ENV = branch
          env.BRANCH  = branch
          def envFile = ".env.${env.APP_ENV}"

          if (!fileExists(envFile)) {
            error "缺少环境配置文件: ${envFile}"
          }

          env.STORE_PORT         = readEnvVar(envFile, 'STORE_PORT', '8001')
          env.GATEWAY_PORT       = readEnvVar(envFile, 'GATEWAY_PORT', '33333')
          env.CONTRACT_PORT      = readEnvVar(envFile, 'CONTRACT_PORT', '8002')
          env.GATEWAY_HOST_PORT  = readEnvVar(envFile, 'GATEWAY_HOST_PORT', env.GATEWAY_PORT)
          env.CONTRACT_HOST_PORT = readEnvVar(envFile, 'CONTRACT_HOST_PORT', '')

          env.DEPLOY_MODE = (env.APP_ENV == 'produ') ? 'ssh' : 'local'

          env.IMAGE_TAG               = "${env.APP_ENV}-${env.BUILD_NUMBER}"
          env.IMAGE_STORE             = "alien_store:${env.IMAGE_TAG}"
          env.IMAGE_GATEWAY           = "alien_gateway:${env.IMAGE_TAG}"
          env.IMAGE_CONTRACT          = "alien_contract:${env.IMAGE_TAG}"
          env.CONTAINER_NAME_STORE    = "alien_store_py-${env.APP_ENV}"
          env.CONTAINER_NAME_GATEWAY  = "alien_gateway_py-${env.APP_ENV}"
          env.CONTAINER_NAME_CONTRACT = "alien_contract_py-${env.APP_ENV}"
          env.DOCKER_NET              = "alien_net_${env.APP_ENV}"

          if (env.DEPLOY_MODE == 'ssh') {
            env.SSH_TARGET      = readEnvVar(envFile, 'DEPLOY_SSH_TARGET')
            env.CODE_DIR_REMOTE = readEnvVar(envFile, 'DEPLOY_CODE_DIR')
            env.LOG_ROOT        = readEnvVar(envFile, 'DEPLOY_LOG_ROOT')
            env.ENV_FILE_REMOTE = "${env.CODE_DIR_REMOTE}/.env.${env.APP_ENV}"
            if (!env.SSH_TARGET || !env.CODE_DIR_REMOTE) {
              error ".env.produ 缺少 DEPLOY_SSH_TARGET 或 DEPLOY_CODE_DIR"
            }
            if (!env.LOG_ROOT) {
              env.LOG_ROOT = "${env.CODE_DIR_REMOTE}/logs"
            }
          } else {
            env.LOG_ROOT = "/docker/python-${env.APP_ENV}/logs"
          }

          echo "APP_ENV=${env.APP_ENV}  DEPLOY_MODE=${env.DEPLOY_MODE}"
          echo "端口 store=${env.STORE_PORT} gateway=${env.GATEWAY_PORT} host=${env.GATEWAY_HOST_PORT} contract=${env.CONTRACT_PORT}"
        }
      }
    }

    stage('Show Build Info') {
      steps {
        script {
          echo "============================================================"
          echo " 部署环境  : ${env.APP_ENV}    （GIT_BRANCH=${env.GIT_BRANCH}）"
          echo " 部署模式  : ${env.DEPLOY_MODE}"
          echo " 镜像 TAG  : ${env.IMAGE_TAG}"
          echo " 容器网络  : ${env.DOCKER_NET}"
          echo " 日志目录  : ${env.LOG_ROOT}"
          if (env.DEPLOY_MODE == 'ssh') {
            echo " SSH 目标  : ${env.SSH_TARGET}"
            echo " 远程目录  : ${env.CODE_DIR_REMOTE}"
          }
          echo "============================================================"
        }
      }
    }

    stage('Verify SSH') {
      when {
        expression { env.DEPLOY_MODE == 'ssh' }
      }
      steps {
        sshagent(credentials: [PROD_SSH_CREDENTIALS_ID]) {
          sh """
            set -e
            ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new '${env.SSH_TARGET}' \\
              'test -f ${env.ENV_FILE_REMOTE} && sudo docker info >/dev/null'
            echo ">>> SSH OK: ${env.SSH_TARGET}"
          """
        }
      }
    }

    stage('Sync Code') {
      when {
        expression { env.DEPLOY_MODE == 'ssh' }
      }
      steps {
        sshagent(credentials: [PROD_SSH_CREDENTIALS_ID]) {
          sh """
            set -e
            ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new '${env.SSH_TARGET}' bash -s <<'REMOTE_EOF'
set -e
cd '${env.CODE_DIR_REMOTE}'
if [ ! -d .git ]; then
  echo "ERROR: ${env.CODE_DIR_REMOTE} is not a git repo"
  exit 1
fi
git fetch origin
git checkout '${env.BRANCH}'
git reset --hard origin/'${env.BRANCH}'
echo ">>> git at \$(git rev-parse --short HEAD) on \$(hostname)"
REMOTE_EOF
          """
        }
      }
    }

    stage('Build Images') {
      steps {
        script {
          def buildCmds = """
sudo docker build --build-arg APP_ENV=${env.APP_ENV} --build-arg STORE_PORT=${env.STORE_PORT} \\
  -f alien_store/Dockerfile -t ${env.IMAGE_STORE} .
sudo docker build --build-arg APP_ENV=${env.APP_ENV} --build-arg GATEWAY_PORT=${env.GATEWAY_PORT} \\
  -f alien_gateway/Dockerfile -t ${env.IMAGE_GATEWAY} .
sudo docker build --build-arg APP_ENV=${env.APP_ENV} --build-arg CONTRACT_PORT=${env.CONTRACT_PORT} \\
  -f alien_contract/Dockerfile -t ${env.IMAGE_CONTRACT} .
""".stripIndent()

          if (env.DEPLOY_MODE == 'ssh') {
            sshagent(credentials: [PROD_SSH_CREDENTIALS_ID]) {
              sh """
                set -e
                ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new '${env.SSH_TARGET}' bash -s <<'REMOTE_EOF'
set -e
cd '${env.CODE_DIR_REMOTE}'
${buildCmds}
echo ">>> images built on \$(hostname)"
REMOTE_EOF
              """
            }
          } else {
            sh """
              set -e
              docker build --build-arg APP_ENV=${env.APP_ENV} --build-arg STORE_PORT=${env.STORE_PORT} \\
                -f alien_store/Dockerfile -t ${env.IMAGE_STORE} .
              docker build --build-arg APP_ENV=${env.APP_ENV} --build-arg GATEWAY_PORT=${env.GATEWAY_PORT} \\
                -f alien_gateway/Dockerfile -t ${env.IMAGE_GATEWAY} .
              docker build --build-arg APP_ENV=${env.APP_ENV} --build-arg CONTRACT_PORT=${env.CONTRACT_PORT} \\
                -f alien_contract/Dockerfile -t ${env.IMAGE_CONTRACT} .
            """
          }
        }
      }
    }

    stage('Deploy') {
      steps {
        script {
          def dockerBin = (env.DEPLOY_MODE == 'ssh') ? 'sudo docker' : 'docker'
          def sshEnvLines = (env.DEPLOY_MODE == 'ssh') ?
            "  --env-file ${env.ENV_FILE_REMOTE} \\\n  -v ${env.ENV_FILE_REMOTE}:/app/.env.${env.APP_ENV}:ro \\\n" : ''
          def contractPublishLine = env.CONTRACT_HOST_PORT?.trim() ?
            "  -p ${env.CONTRACT_HOST_PORT}:${env.CONTRACT_PORT} \\\n" : ''

          def legacyCleanup = (env.APP_ENV == 'produ') ? """
${dockerBin} rm -f py_esign_produ py_gateway_produ py_contract_produ esign alien_gateway_py alien_contract_py \\
  alien_store_produ alien_gateway_produ alien_contract_produ 2>/dev/null || true
""" : ''

          def deployScript = """
set -e
${dockerBin} network create ${env.DOCKER_NET} 2>/dev/null || true
mkdir -p ${env.LOG_ROOT}/store ${env.LOG_ROOT}/gateway ${env.LOG_ROOT}/contract

${dockerBin} rm -f ${env.CONTAINER_NAME_STORE} ${env.CONTAINER_NAME_GATEWAY} ${env.CONTAINER_NAME_CONTRACT} 2>/dev/null || true
${legacyCleanup}
${dockerBin} run -d --name ${env.CONTAINER_NAME_STORE} \\
  --network ${env.DOCKER_NET} \\
${sshEnvLines}  -e APP_ENV=${env.APP_ENV} \\
  -e STORE_PORT=${env.STORE_PORT} \\
  -v ${env.LOG_ROOT}/store:/app/common/logs/alien_store \\
  --restart unless-stopped \\
  ${env.IMAGE_STORE}

${dockerBin} run -d --name ${env.CONTAINER_NAME_CONTRACT} \\
  --network ${env.DOCKER_NET} \\
${contractPublishLine}${sshEnvLines}  -e APP_ENV=${env.APP_ENV} \\
  -e CONTRACT_PORT=${env.CONTRACT_PORT} \\
  -v ${env.LOG_ROOT}/contract:/app/common/logs/alien_contract \\
  --restart unless-stopped \\
  ${env.IMAGE_CONTRACT}

${dockerBin} run -d --name ${env.CONTAINER_NAME_GATEWAY} \\
  --network ${env.DOCKER_NET} \\
  -p ${env.GATEWAY_HOST_PORT}:${env.GATEWAY_PORT} \\
${sshEnvLines}  -e APP_ENV=${env.APP_ENV} \\
  -e GATEWAY_PORT=${env.GATEWAY_PORT} \\
  -e STORE_BASE_URL=http://${env.CONTAINER_NAME_STORE}:${env.STORE_PORT} \\
  -e CONTRACT_BASE_URL=http://${env.CONTAINER_NAME_CONTRACT}:${env.CONTRACT_PORT} \\
  -v ${env.LOG_ROOT}/gateway:/app/common/logs/alien_gateway \\
  --restart unless-stopped \\
  ${env.IMAGE_GATEWAY}
"""

          if (env.DEPLOY_MODE == 'ssh') {
            sshagent(credentials: [PROD_SSH_CREDENTIALS_ID]) {
              sh """
                set -e
                ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new '${env.SSH_TARGET}' bash -s <<'REMOTE_EOF'
${deployScript}
REMOTE_EOF
              """
            }
          } else {
            sh deployScript
          }
        }
      }
    }

    stage('Smoke Test') {
      steps {
        script {
          sleep(time: 5, unit: 'SECONDS')

          def containers = [
            env.CONTAINER_NAME_STORE,
            env.CONTAINER_NAME_CONTRACT,
            env.CONTAINER_NAME_GATEWAY
          ]

          if (env.DEPLOY_MODE == 'ssh') {
            sshagent(credentials: [PROD_SSH_CREDENTIALS_ID]) {
              def allRunning = true
              containers.each { c ->
                def status = sh(
                  returnStdout: true,
                  script: """
                    ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new '${env.SSH_TARGET}' \\
                      "sudo docker inspect -f '{{.State.Status}}' ${c} 2>/dev/null || echo missing"
                  """
                ).trim()
                echo "  ${c}: ${status}"
                if (status != 'running') { allRunning = false }
              }
              if (!allRunning) {
                error "存在容器未处于 running 状态，部署失败"
              }
              def rc = sh(returnStatus: true, script: """
                ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new '${env.SSH_TARGET}' \\
                  "sudo docker exec ${env.CONTAINER_NAME_GATEWAY} python -c \\"import urllib.request as u; r=u.urlopen('http://localhost:${env.GATEWAY_PORT}/health', timeout=3); print('HTTP', r.status)\\""
              """)
              if (rc == 0) {
                echo "✓ gateway /health 通过"
              } else {
                echo "⚠️ gateway /health 未通过，请手动验证：curl http://<host>:${env.GATEWAY_HOST_PORT}/health"
              }
            }
          } else {
            def allRunning = true
            containers.each { c ->
              def status = sh(
                returnStdout: true,
                script: "docker inspect -f '{{.State.Status}}' ${c} 2>/dev/null || echo missing"
              ).trim()
              echo "  ${c}: ${status}"
              if (status != 'running') { allRunning = false }
            }
            if (!allRunning) {
              error "存在容器未处于 running 状态，部署失败"
            }
            echo "✓ 3 个业务容器均在 running 状态"
            def rc = sh(returnStatus: true, script: """
              docker exec ${env.CONTAINER_NAME_GATEWAY} python -c 'import urllib.request as u; r=u.urlopen("http://localhost:${env.GATEWAY_PORT}/health", timeout=3); print("HTTP", r.status, r.read(200).decode())'
            """)
            if (rc == 0) {
              echo "✓ gateway /health 通过"
            } else {
              echo "⚠️ gateway /health 未通过，请手动验证：curl http://<host>:${env.GATEWAY_HOST_PORT}/health"
            }
          }
        }
      }
    }
  }

  post {
    success {
      echo "[${env.APP_ENV}] 部署成功！访问 http://<host>:${env.GATEWAY_HOST_PORT}/health"
    }
    failure {
      echo "[${env.APP_ENV}] 部署失败，请检查日志。"
    }
    always {
      cleanWs()
    }
  }
}
