name: Preview Deploy
on:
  pull_request:
    types:
    - opened
    - synchronize
    - reopened
    branches:
    - main
    - develop
  workflow_dispatch:
    inputs:
      reason:
        description: "触发原因"
        required: false
        default: "手动触发 - 预览环境补跑"
permissions:
  contents: read
  pull-requests: write
concurrency:
  group: preview-deploy-${{ gitea.ref }}
  cancel-in-progress: true
jobs:
  deploy-preview:
    name: Deploy Preview Environment
    runs-on: runtime-builder
    timeout-minutes: 20
    steps:
    - name: Checkout code
      shell: sh
      env:
        GITHUB_TOKEN: ${{ github.token }}
      run: |
        set -eu
        python3 - <<'PY'
        import io, os, tarfile, time, urllib.request, urllib.error
        url = f"{os.environ['GITHUB_API_URL']}/repos/{os.environ['GITHUB_REPOSITORY']}/archive/{os.environ['GITHUB_SHA']}.tar.gz"
        request = urllib.request.Request(url, headers={"Authorization": f"token {os.environ['GITHUB_TOKEN']}"})
        last_err = None
        for attempt in range(5):
            try:
                with urllib.request.urlopen(request, timeout=120) as response:
                    archive = response.read()
                break
            except urllib.error.HTTPError as e:
                last_err = e
                if e.code >= 500 and attempt < 4:
                    wait = 2 ** attempt
                    print(f"Checkout HTTP {e.code}, retrying in {wait}s (attempt {attempt+1}/5)...")
                    time.sleep(wait)
                    continue
                raise
            except Exception as e:
                last_err = e
                if attempt < 4:
                    wait = 2 ** attempt
                    print(f"Checkout error: {e}, retrying in {wait}s (attempt {attempt+1}/5)...")
                    time.sleep(wait)
                    continue
                raise
        else:
            raise last_err
        with tarfile.open(fileobj=io.BytesIO(archive), mode='r:gz') as tar:
            root_prefix = tar.getmembers()[0].name.split('/', 1)[0] + '/'
            for member in tar.getmembers():
                name = member.name
                if name == root_prefix[:-1]:
                    continue
                if name.startswith(root_prefix):
                    member.name = name[len(root_prefix):]
                if member.name:
                    tar.extract(member, '.')
        PY

    - name: Record job start time
      shell: sh
      run: |
        set -eu
        echo "JOB_START_TIME=$(date +%s)" >> $GITHUB_ENV
        echo "Job started at $(date)"

    - name: Extract PR number
      shell: sh
      run: |
        set -eu
        PR_NUMBER=$(echo "$GITHUB_REF" | sed 's|refs/pull/||; s|/.*||')
        echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
        echo "PR number: $PR_NUMBER"
        echo "PREVIEW_URL=https://pr-${PR_NUMBER}.preview.xiaoxiajianji.com" >> $GITHUB_ENV
        echo "Preview URL: https://pr-${PR_NUMBER}.preview.xiaoxiajianji.com"

    - name: Build frontend
      shell: sh
      run: |
        set -eu
        NPM_CACHE_VOLUME="xiaoxia-npm-cache"
        if ! docker volume inspect "$NPM_CACHE_VOLUME" >/dev/null 2>&1; then
          docker volume create "$NPM_CACHE_VOLUME" >/dev/null
          echo "Created npm cache volume: $NPM_CACHE_VOLUME"
        fi

        docker run --rm \
          -v "$PWD:/workspace" \
          -v "$NPM_CACHE_VOLUME:/workspace/apps/web/node_modules" \
          -w /workspace/apps/web \
          -e VITE_API_URL=https://staging-api.xiaoxiajianji.com \
          docker.m.daocloud.io/library/node:20 \
          sh -lc '
            PACKAGE_LOCK_HASH=$(md5sum package-lock.json 2>/dev/null | cut -d" " -f1)
            CACHE_HASH_FILE="node_modules/.package-lock-hash"
            CACHE_VALID=false
            if [ -f "$CACHE_HASH_FILE" ] && [ "$(cat "$CACHE_HASH_FILE")" = "$PACKAGE_LOCK_HASH" ] && [ -x "node_modules/.bin/vite" ] && [ -x "node_modules/.bin/tsc" ]; then
              CACHE_VALID=true
              echo "Cache hit: dependencies valid, skipping npm ci"
            fi
            if [ "$CACHE_VALID" = "false" ]; then
              echo "Cache miss or invalid: running npm ci..."
              if ! npm ci --include=dev; then
                echo "npm ci failed, cleaning node_modules and retrying..."
                rm -rf node_modules
                mkdir -p node_modules
                npm ci --include=dev
              fi
              echo "$PACKAGE_LOCK_HASH" > "$CACHE_HASH_FILE"
              echo "Dependencies installed, cache updated"
            fi
            echo "Running TypeScript check..."
            npx --no-install tsc
            echo "Running Vite build..."
            npx --no-install vite build
            echo "Build completed successfully"
            ls -la dist/
          '

    - name: Install SSH client and rsync
      shell: sh
      run: |
        set -eu
        if command -v apk >/dev/null 2>&1; then
          apk add --no-cache openssh-client rsync >/dev/null 2>&1
        elif command -v apt-get >/dev/null 2>&1; then
          apt-get update -qq && apt-get install -y -qq openssh-client rsync >/dev/null 2>&1
        elif command -v yum >/dev/null 2>&1; then
          yum install -y openssh-clients rsync >/dev/null 2>&1
        else
          echo "ERROR: No package manager found"
          exit 1
        fi
        echo "openssh-client and rsync installed"

    - name: Deploy preview to server
      shell: sh
      env:
        PREVIEW_SSH_HOST: ${{ secrets.PREVIEW_SSH_HOST }}
        PREVIEW_SSH_USER: ${{ secrets.PREVIEW_SSH_USER }}
        PREVIEW_SSH_PORT: ${{ secrets.PREVIEW_SSH_PORT }}
        PREVIEW_SSH_KEY: ${{ secrets.PREVIEW_SSH_KEY }}
      run: |
        set -eux
        preview_host="${PREVIEW_SSH_HOST:-47.98.113.167}"
        preview_user="${PREVIEW_SSH_USER:-root}"
        preview_port="${PREVIEW_SSH_PORT:-22222}"
        preview_dir="/var/www/preview/pr-${PR_NUMBER}"

        mkdir -p ~/.ssh

        # 查找可用的SSH密钥（优先用 secret 里专门为 preview 配置的 key）
        key_path=""
        if [ -n "${PREVIEW_SSH_KEY:-}" ]; then
          key_path="$HOME/.ssh/id_ed25519"
          printf '%s\n' "$PREVIEW_SSH_KEY" > "$key_path"
          chmod 600 "$key_path"
          echo "Using key from PREVIEW_SSH_KEY secret"
        elif [ -f /root/.ssh/xiaoxia_runtime_builder ]; then
          key_path="/root/.ssh/xiaoxia_runtime_builder"
          echo "Using key: $key_path (builder key)"
        elif [ -f "$HOME/.ssh/xiaoxia_runtime_builder" ]; then
          key_path="$HOME/.ssh/xiaoxia_runtime_builder"
          echo "Using key: $key_path (home key)"
        else
          echo "ERROR: No SSH key available"
          ls -la ~/.ssh/ 2>/dev/null || true
          ls -la /root/.ssh/ 2>/dev/null || true
          exit 1
        fi

        # SSH密钥完整性自检
        if ! ssh-keygen -y -f "$key_path" > /dev/null 2>&1; then
          echo "ERROR: SSH密钥损坏（private key contents do not match public）"
          echo "请检查 PREVIEW_SSH_KEY secret 中的私钥是否完整正确"
          echo "私钥文件大小: $(wc -c < "$key_path") 字节"
          head -2 "$key_path"
          exit 1
        fi
        echo "SSH key integrity check passed"

        ssh-keyscan -p "$preview_port" -H "$preview_host" >> ~/.ssh/known_hosts 2>/dev/null
        echo "SSH keyscan done"

        # 测试SSH连接
        ssh -p "$preview_port" -i "$key_path" -o StrictHostKeyChecking=no "${preview_user}@${preview_host}" "echo SSH_CONNECTION_OK && hostname"
        echo "SSH connection verified"

        # 创建预览目录并上传文件
        ssh -p "$preview_port" -i "$key_path" -o StrictHostKeyChecking=no "${preview_user}@${preview_host}" \
          "mkdir -p ${preview_dir} && echo 'Preview directory created: ${preview_dir}'"

        # 使用rsync上传dist目录内容
        rsync -avz --delete -e "ssh -p ${preview_port} -i ${key_path} -o StrictHostKeyChecking=no" \
          apps/web/dist/ \
          "${preview_user}@${preview_host}:${preview_dir}/"

        echo "Preview deployed to: ${preview_dir}"
        echo "Preview URL: https://pr-${PR_NUMBER}.preview.xiaoxiajianji.com"

    - name: Comment preview link on PR
      shell: sh
      env:
        GITHUB_TOKEN: ${{ github.token }}
      run: |
        set -eu
        PR_NUMBER=$(echo "$GITHUB_REF" | sed 's|refs/pull/||; s|/.*||')
        PREVIEW_URL="https://pr-${PR_NUMBER}.preview.xiaoxiajianji.com"
        export PR_NUMBER PREVIEW_URL

        COMMENT_BODY=$(python3 scripts/ci/preview_comment.py deploy)

        API_URL="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments"

        EXISTING_COMMENT_ID=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "$API_URL" | python3 -c "
        import sys, json
        try:
            for c in json.load(sys.stdin):
                if '预览环境已部署' in c.get('body', ''):
                    print(c['id'])
                    break
        except Exception:
            pass
        ")

        if [ -n "$EXISTING_COMMENT_ID" ]; then
          curl -s -X PATCH \
            -H "Authorization: token ${GITHUB_TOKEN}" \
            -H "Content-Type: application/json" \
            -d "$COMMENT_BODY" \
            "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/comments/${EXISTING_COMMENT_ID}" \
            > /dev/null
          echo "Comment updated"
        else
          curl -s -X POST \
            -H "Authorization: token ${GITHUB_TOKEN}" \
            -H "Content-Type: application/json" \
            -d "$COMMENT_BODY" \
            "$API_URL" \
            > /dev/null
          echo "Comment posted"
        fi

    - name: Job duration summary
      if: always()
      shell: sh
      run: |
        set +eu
        if [ -n "$JOB_START_TIME" ]; then
          END_TIME=$(date +%s)
          DURATION=$((END_TIME - JOB_START_TIME))
          MINS=$((DURATION / 60))
          SECS=$((DURATION % 60))
          echo "JOB_DURATION_SECONDS=$DURATION" >> $GITHUB_ENV
          echo "=== Job Duration: ${MINS}m${SECS}s ==="
        else
          echo "JOB_DURATION_SECONDS=0" >> $GITHUB_ENV
          echo "=== Job Duration: unknown ==="
        fi

    - name: Notify on failure
      continue-on-error: true
      if: failure()
      shell: sh
      env:
        CI_NOTIFY_WEBHOOK: ${{ secrets.CI_NOTIFY_WEBHOOK }}
      run: |
        set +e
        NOTIFY_MODE=failure JOB_NAME="Deploy Preview Environment" python3 scripts/ci_notify.py
    - name: Report CI trace
      if: always()
      shell: sh
      env:
        AGENTLOOP_LICENSE_KEY: ${{ secrets.AGENTLOOP_LICENSE_KEY }}
      run: |
        STATUS="ok"
        [ ${{ job.status }} = "success" ] || STATUS="error"
        START_TIME=""
        [ -f /tmp/ci_job_start_time ] && START_TIME=$(cat /tmp/ci_job_start_time)
        python3 scripts/ci/ci_trace_report.py --service xiaoxia-saas-ci --status $STATUS --start-time "$START_TIME" || true

