name: autograde-final-vibevault

on:
  push:
    branches:
      - main
    tags:
      - 'submit'        # 仍然允许标签触发
      - 'submit-*'
  workflow_dispatch:

permissions:
  contents: read
  pull-requests: write

jobs:
  # 检查是否应该触发 CI（仅在 commit message 包含 "完成作业" 时执行）
  check-trigger:
    runs-on: docker
    container:
      image: alpine:latest
    outputs:
      should_run: ${{ steps.check.outputs.trigger }}
    steps:
      - name: Check commit message for trigger keyword
        id: check
        run: |
          COMMIT_MSG="${{ github.event.head_commit.message || '' }}"
          echo "Commit message: $COMMIT_MSG"
          if echo "$COMMIT_MSG" | grep -q "完成作业"; then
            echo "trigger=true" >> $GITHUB_OUTPUT
            echo "✅ Commit contains \"完成作业\"，即将执行评分"
          else
            echo "trigger=false" >> $GITHUB_OUTPUT
            echo "⛔ 只有包含"完成作业"的提交才会执行自动评分" >&2
          fi

  grade:
    needs: check-trigger
    if: needs.check-trigger.outputs.should_run == 'true'
    runs-on: docker
    container:
      image: gradle:9.0-jdk21
      options: --user root
    timeout-minutes: 30
    
    steps:
      - name: Install dependencies (CN mirror)
        run: |
          set -e
          # 替换 Debian/Ubuntu 源为阿里云镜像
          for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do
            [ -f "$f" ] || continue
            sed -i -E 's|https?://deb.debian.org|http://mirrors.aliyun.com|g' "$f" || true
            sed -i -E 's|https?://security.debian.org|http://mirrors.aliyun.com/debian-security|g' "$f" || true
            sed -i -E 's|https?://archive.ubuntu.com|http://mirrors.aliyun.com|g' "$f" || true
            sed -i -E 's|https?://ports.ubuntu.com|http://mirrors.aliyun.com|g' "$f" || true
          done
          apt-get -o Acquire::Check-Valid-Until=false update -y
          DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git ca-certificates python3 python3-pip rsync \
            libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info \
            fonts-noto-cjk fonts-wqy-microhei
          pip3 install --break-system-packages python-dotenv requests markdown weasyprint -i https://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com
          # 刷新字体缓存
          fc-cache -f -v > /dev/null 2>&1 || true
          rm -rf /var/lib/apt/lists/*
      
      - name: Configure Gradle mirror (Aliyun)
        run: |
          mkdir -p ~/.gradle
          cat > ~/.gradle/init.gradle << 'EOF'
allprojects {
    repositories {
        mavenLocal()
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url 'https://maven.aliyun.com/repository/spring' }
        maven { url 'https://maven.aliyun.com/repository/spring-plugin' }
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
        mavenCentral()
    }
}
EOF
          echo "✅ Gradle configured to use Aliyun mirror"
      
      - name: Checkout code
        env:
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          git config --global --add safe.directory ${{ github.workspace }}
          git init
          # Use token for authentication (required for private repos)
          REPO_URL="${{ github.server_url }}/${{ github.repository }}.git"
          AUTH_URL=$(echo "$REPO_URL" | sed "s|://|://${GITHUB_TOKEN}@|")
          git remote add origin "$AUTH_URL"
          git fetch --depth=1 origin ${{ github.sha }}
          git checkout ${{ github.sha }}
      
      - name: Fix permissions
        run: chown -R $(whoami):$(whoami) ${{ github.workspace }} || true

      - name: Fetch hidden tests and grading scripts
        working-directory: ${{ github.workspace }}
        env:
          EXTERNAL_GITEA_HOST: ${{ secrets.EXTERNAL_GITEA_HOST }}
        run: |
          # Allow this step to fail gracefully without aborting the entire workflow
          set +e
          
          echo "📥 Checking for hidden tests and grading scripts..."
          
          # Check if local autograde directory already exists
          if [ -d ".autograde" ]; then
            echo "⚠️  Found existing .autograde directory - using local scripts"
            
            # Verify we have required grading scripts
            if [ ! -f ".autograde/grade_grouped.py" ]; then
              echo "❌ Missing required grading script: .autograde/grade_grouped.py"
              echo "Creating minimal grade.json file to continue..."
              echo '{"total": 0, "groups": [], "raw_scores": {}}' > grade.json
              exit 0
            fi
            
            # Verify we have test groups file
            if [ ! -f "test_groups.json" ]; then
              echo "❌ Missing test_groups.json file"
              echo "Creating minimal test_groups.json file to continue..."
              echo '{"groups": []}' > test_groups.json
            fi
            
            echo "✅ Using local grading scripts"
            exit 0
          fi
          
          # Proceed with fetching from external repo if local scripts not found
          GITEA_TOKEN="${{ secrets.GITEA_TOKEN }}"
          
          # Resolve Gitea Host - 修复关键部分开始
          if [ -n "$EXTERNAL_GITEA_HOST" ]; then
            HOST="$EXTERNAL_GITEA_HOST"
          elif [ -n "$GITEA_ROOT_URL" ]; then
            HOST=$(echo "$GITEA_ROOT_URL" | sed 's|https\?://||' | sed 's|/$||')
          else
            # 直接指定正确的 Gitea 服务器地址和端口
            HOST="49.234.193.192:3000"
          fi
          
          ORG=$(echo "${{ github.repository }}" | cut -d'/' -f1)
          REPO_NAME=$(echo "${{ github.repository }}" | cut -d'/' -f2)
          
          # Extract assignment ID - 确保拼写正确
          if echo "$REPO_NAME" | grep -q -- '-stu_'; then
            ASSIGNMENT_ID=$(echo "$REPO_NAME" | sed 's/-stu_.*//')
          elif echo "$REPO_NAME" | grep -q -- '-template'; then
            ASSIGNMENT_ID=$(echo "$REPO_NAME" | sed 's/-template.*//')
          else
            # 使用正确的作业ID
            ASSIGNMENT_ID="final-vibevault"
          fi
          
          # 直接使用正确的测试仓库名（修复拼写错误）
          TEST_REPO_NAMES=("final-vibevault-tests" "${ASSIGNMENT_ID}-tests" "tests-${ASSIGNMENT_ID}" "grading-${ASSIGNMENT_ID}")
          
          echo "🔍 Looking for test repos at host: $HOST, org: $ORG"
          
          for TEST_REPO in "${TEST_REPO_NAMES[@]}"; do
            echo "📥 Trying to fetch from ${ORG}/${TEST_REPO}..."
            
            # Try with different authentication methods
            if [ -n "$GITEA_TOKEN" ]; then
              AUTH_URL="http://git:${GITEA_TOKEN}@${HOST}/${ORG}/${TEST_REPO}.git"
              echo "Using GITEA_TOKEN for authentication..."
            else
              AUTH_URL="http://${HOST}/${ORG}/${TEST_REPO}.git"
              echo "Using no authentication..."
            fi
            
            echo "Clone URL: $AUTH_URL"
            
            if git -c http.sslVerify=false clone --depth=1 "$AUTH_URL" _priv_tests 2>&1; then
              echo "✅ Successfully fetched hidden tests and grading scripts from ${ORG}/${TEST_REPO}"
              
              # Continue with processing the fetched tests
              SUCCESS=0
              
              if [ -d "_priv_tests/autograde" ]; then
                # Remove any local .autograde (prevent student tampering)
                rm -rf .autograde
                mkdir -p .autograde
                cp _priv_tests/autograde/*.py .autograde/
                cp _priv_tests/autograde/*.sh .autograde/ 2>/dev/null || true
                echo "✅ Grading scripts copied from tests repo"
                SUCCESS=1
              else
                echo "⚠️  No autograde directory in tests repo!"
              fi
              
              # Copy Java tests
              if [ -d "_priv_tests/java/src/test" ]; then
                rsync -a _priv_tests/java/src/test/ src/test/
                echo "✅ Private tests copied"
              fi
              
              # Copy test_groups.json if exists
              if [ -f "_priv_tests/test_groups.json" ]; then
                cp _priv_tests/test_groups.json .
                echo "✅ test_groups.json copied"
              fi
              
              # Copy LLM rubrics
              if [ -d "_priv_tests/llm" ]; then
                mkdir -p .llm_rubrics
                cp _priv_tests/llm/*.json .llm_rubrics/ 2>/dev/null || true
                echo "✅ LLM rubrics copied"
              fi
              
              # Cleanup
              rm -rf _priv_tests
              
              if [ $SUCCESS -eq 1 ]; then
                exit 0
              fi
            else
              echo "❌ Failed to clone ${ORG}/${TEST_REPO}"
            fi
          done
          
          # If all attempts failed, create minimal required files to continue
          echo "⚠️  All attempts to fetch hidden tests failed (non-fatal)"
          echo "This could be due to:"
          echo "1. Test repository not available or named differently"
          echo "2. Missing or invalid authentication token"
          echo "3. Repository access restrictions"
          echo "4. Network connectivity issues"
          echo ""
          echo "Creating minimal required files to continue with public tests only..."
          
          # Create minimal .autograde directory and scripts
          mkdir -p .autograde
          
          # Create minimal grade_grouped.py script
          cat > .autograde/grade_grouped.py << 'EOF'
import json
import sys
import argparse

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--junit-dir')
    parser.add_argument('--groups')
    parser.add_argument('--out')
    parser.add_argument('--summary')
    args = parser.parse_args()
    
    # Create minimal grade output
    grade_data = {
        "total": 0,
        "groups": [],
        "raw_scores": {}
    }
    
    with open(args.out, 'w') as f:
        json.dump(grade_data, f)
    
    with open(args.summary, 'w') as f:
        f.write("# Grading Summary\n\n")
        f.write("⚠️  Only public tests were executed (no hidden tests available)\n\n")
        f.write("## Results\n\n")
        f.write("- Public tests: Executed\n")
        f.write("- Hidden tests: Not available\n")

if __name__ == "__main__":
    main()
EOF
          
          # Create minimal test_groups.json
          echo '{"groups": []}' > test_groups.json
          
          echo "✅ Created minimal required files"
          echo "Continuing with public tests only..."
      
      - name: Run tests
        working-directory: ${{ github.workspace }}
        run: |
          gradle test --no-daemon || true
          
          # Collect all JUnit XML reports
          find build/test-results/test -name "TEST-*.xml" -exec cat {} \; > all_tests.xml 2>/dev/null || true
          
          # Also try to get a single combined report
          if [ -f build/test-results/test/TEST-*.xml ]; then
            cp build/test-results/test/TEST-*.xml junit.xml 2>/dev/null || true
          fi
      
      - name: Grade programming tests
        working-directory: ${{ github.workspace }}
        run: |
          # Use extended grading script with group support
          python3 ./.autograde/grade_grouped.py \
            --junit-dir build/test-results/test \
            --groups test_groups.json \
            --out grade.json \
            --summary summary.md
      
      - name: Grade REPORT.md
        working-directory: ${{ github.workspace }}
        run: |
          # LLM env vars are injected by Runner config (LLM_API_KEY, LLM_API_URL, LLM_MODEL)
          if [ -f REPORT.md ] && [ -f .llm_rubrics/rubric_report.json ]; then
            python3 ./.autograde/llm_grade.py \
              --question "请评估这份后端与系统设计报告" \
              --answer REPORT.md \
              --rubric .llm_rubrics/rubric_report.json \
              --out report_grade.json \
              --summary report_summary.md
            echo "✅ REPORT.md graded"
          else
            echo '{"total": 0, "flags": ["missing_file"]}' > report_grade.json
            echo "⚠️ REPORT.md or rubric not found"
          fi
      
      - name: Grade FRONTEND.md
        working-directory: ${{ github.workspace }}
        run: |
          # LLM env vars are injected by Runner config (LLM_API_KEY, LLM_API_URL, LLM_MODEL)
          if [ -f FRONTEND.md ] && [ -f .llm_rubrics/rubric_frontend.json ]; then
            python3 ./.autograde/llm_grade.py \
              --question "请评估这份前端界面与交互设计报告" \
              --answer FRONTEND.md \
              --rubric .llm_rubrics/rubric_frontend.json \
              --out frontend_grade.json \
              --summary frontend_summary.md
            echo "✅ FRONTEND.md graded"
          else
            echo '{"total": 0, "flags": ["missing_file"]}' > frontend_grade.json
            echo "⚠️ FRONTEND.md or rubric not found"
          fi
      
      - name: Aggregate grades
        working-directory: ${{ github.workspace }}
        run: |
          python3 ./.autograde/aggregate_final_grade.py \
            --programming grade.json \
            --report report_grade.json \
            --frontend frontend_grade.json \
            --out final_grade.json \
            --summary final_summary.md
      
      - name: Generate PDF report
        working-directory: ${{ github.workspace }}
        env:
          REPO: ${{ github.repository }}
          COMMIT_SHA: ${{ github.sha }}
        run: |
          if [ -f final_grade.json ]; then
            # 读取学生信息文件（如果存在）
            STUDENT_ID=""
            STUDENT_NAME=""
            CLASS_NAME=""
            
            if [ -f .student_info.json ]; then
              STUDENT_ID=$(python3 -c "import json; d=json.load(open('.student_info.json')); print(d.get('student_id',''))" 2>/dev/null || echo "")
              STUDENT_NAME=$(python3 -c "import json; d=json.load(open('.student_info.json')); print(d.get('name',''))" 2>/dev/null || echo "")
              CLASS_NAME=$(python3 -c "import json; d=json.load(open('.student_info.json')); print(d.get('class_name',''))" 2>/dev/null || echo "")
            fi
            
            # 如果没有学生信息文件，从仓库名提取学号
            if [ -z "$STUDENT_ID" ]; then
              STUDENT_ID=$(echo "$REPO" | sed -n 's/.*-stu[_-]\?\(st\)\?\([0-9]*\)$/\2/p')
            fi
            
            python3 ./.autograde/generate_pdf_report.py \
              --report REPORT.md \
              --frontend FRONTEND.md \
              --grade final_grade.json \
              --images images \
              --out grade_report.pdf \
              --student-id "$STUDENT_ID" \
              --student-name "$STUDENT_NAME" \
              --class-name "$CLASS_NAME" \
              --commit-sha "$COMMIT_SHA"
          fi
      
      - name: Upload report to student repo
        if: env.RUNNER_METADATA_TOKEN != ''
        working-directory: ${{ github.workspace }}
        env:
          TOKEN: ${{ env.RUNNER_METADATA_TOKEN }}
          REPO: ${{ github.repository }}
          SERVER_URL: ${{ github.server_url }}
          COMMIT_SHA: ${{ github.sha }}
        run: |
          # 上传 PDF 或 Markdown 报告到学生仓库
          REPORT_FILE=""
          if [ -f grade_report.pdf ]; then
            REPORT_FILE="grade_report.pdf"
          elif [ -f grade_report.md ]; then
            REPORT_FILE="grade_report.md"
          fi
          
          if [ -n "$REPORT_FILE" ]; then
            # 使用内部地址
            API_URL="http://gitea:3000/api/v1"
            SHORT_SHA=$(echo "$COMMIT_SHA" | cut -c1-7)
            DEST_PATH="reports/grade_report_${SHORT_SHA}.${REPORT_FILE##*.}"
            
            # Base64 编码并保存到临时文件（避免命令行参数过长）
            CONTENT=$(base64 -w 0 "$REPORT_FILE")
            
            # 创建请求 JSON 文件
            cat > /tmp/upload_request.json << EOF
{"message": "Add grade report for $SHORT_SHA", "content": "$CONTENT"}
EOF
            
            # 先尝试 POST 创建新文件
            RESULT=$(curl -s -X POST -H "Authorization: token $TOKEN" \
              -H "Content-Type: application/json" \
              "$API_URL/repos/$REPO/contents/$DEST_PATH" \
              -d @/tmp/upload_request.json)
            
            if echo "$RESULT" | grep -q '"content"'; then
              echo "✅ Report uploaded to $DEST_PATH"
            else
              # POST 失败，可能文件已存在，尝试获取 SHA 并 PUT 更新
              echo "POST failed, trying PUT with SHA..."
              SHA=$(curl -s -H "Authorization: token $TOKEN" \
                "$API_URL/repos/$REPO/contents/$DEST_PATH" \
                | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('sha','') if isinstance(d,dict) and 'sha' in d else '')" 2>/dev/null || echo "")
              
              if [ -n "$SHA" ]; then
                # 创建更新请求 JSON 文件
                cat > /tmp/upload_request.json << EOF
{"message": "Update grade report for $SHORT_SHA", "content": "$CONTENT", "sha": "$SHA"}
EOF
                
                RESULT=$(curl -s -X PUT -H "Authorization: token $TOKEN" \
                  -H "Content-Type: application/json" \
                  "$API_URL/repos/$REPO/contents/$DEST_PATH" \
                  -d @/tmp/upload_request.json)
                
                if echo "$RESULT" | grep -q '"content"'; then
                  echo "✅ Report updated at $DEST_PATH"
                else
                  echo "⚠️ Failed to update report: $RESULT"
                fi
              else
                echo "⚠️ Could not get file SHA, upload failed"
              fi
            fi
            
            # 清理临时文件
            rm -f /tmp/upload_request.json
          fi
      
      - name: Create metadata
        working-directory: ${{ github.workspace }}
        env:
          REPO: ${{ github.repository }}
        run: |
          if [ -f final_grade.json ]; then
            export GRADE_TYPE=final
            python3 ./.autograde/create_minimal_metadata.py > metadata.json || echo "{}" > metadata.json
          fi
      
      - name: Upload metadata
        if: env.RUNNER_METADATA_TOKEN != ''
        working-directory: ${{ github.workspace }}
        env:
          # 使用当前组织的 course-metadata 仓库，而不是 Runner 配置中的硬编码值
          METADATA_REPO: ${{ github.repository_owner }}/course-metadata
          METADATA_TOKEN: ${{ env.RUNNER_METADATA_TOKEN }}
          METADATA_BRANCH: ${{ env.RUNNER_METADATA_BRANCH }}
          STUDENT_REPO: ${{ github.repository }}
          RUN_ID: ${{ github.run_id }}
          COMMIT_SHA: ${{ github.sha }}
          SERVER_URL: ${{ github.server_url }}
        run: |
          if [ -f metadata.json ]; then
            python3 ./.autograde/upload_metadata.py \
              --metadata-file metadata.json \
              --metadata-repo "${METADATA_REPO}" \
              --branch "${METADATA_BRANCH:-main}" \
              --student-repo "${STUDENT_REPO}" \
              --run-id "${RUN_ID}" \
              --commit-sha "${COMMIT_SHA}" \
              --workflow grade \
              --server-url "${SERVER_URL}" \
              --external-host "${EXTERNAL_GITEA_HOST}"
          fi