#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────
# Scenario 2: Code Quality Gatekeeper
# Flow: PR submit → Load Skill → Auto Review → Check CI → Auto-merge
#
# Commands/Skills chained:
#   1. pr +list              -- list open PRs
#   2. pr +view              -- get PR details
#   3. pr +files             -- get changed files
#   4. pr +diff              -- get diff content
#   5. gitlink-code-review   -- AI code review (skill-driven)
#   6. api POST .../reviews  -- post review comment with scores
#   7. ci +builds            -- check CI build status
#   8. pr +merge             -- auto-merge if quality passes threshold
# ─────────────────────────────────────────────────────────────────────

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/lib/common.sh"

usage() {
    echo "Usage: $0 --owner OWNER --repo REPO [--pr-id ID] [--threshold SCORE] [--dry-run]"
    echo ""
    echo "  --owner OWNER        Repository owner"
    echo "  --repo  REPO         Repository name"
    echo "  --pr-id ID           Specific PR to review (default: all open PRs)"
    echo "  --threshold SCORE    Min quality score to auto-merge (default: 80)"
    echo "  --dry-run            Preview actions without executing"
    exit 1
}

THRESHOLD=80
DRY_RUN=false
OWNER=""
REPO=""
PR_ID=""

while [[ $# -gt 0 ]]; do
    case "$1" in
        --owner)     OWNER="$2"; shift 2 ;;
        --repo)      REPO="$2"; shift 2 ;;
        --pr-id)     PR_ID="$2"; shift 2 ;;
        --threshold) THRESHOLD="$2"; shift 2 ;;
        --dry-run)   DRY_RUN="true"; shift ;;
        --help|-h)   usage ;;
        *)           log_err "Unknown arg: $1"; usage ;;
    esac
done

check_auth
require_owner_repo

# ── Review a single PR ───────────────────────────────────────────────
review_pr() {
    local pr_id="$1"

    log_title "Reviewing PR #$pr_id"

    # Initialize arrays
    ISSUES_FOUND=()
    AI_POSITIVE=()
    AI_RECOMMENDATIONS=()

    # Step 1: Get PR details
    log_step "Fetching PR details..."
    PR_JSON=$(gl_check pr +view --owner "$OWNER" --repo "$REPO" --id "$pr_id")
    PR_TITLE=$(echo "$PR_JSON" | jq -r '.data.title // .data.subject // .data.issue.subject // "N/A"')
    PR_STATE=$(echo "$PR_JSON" | jq -r '.data.state // .data.status // "N/A"')
    PR_AUTHOR=$(echo "$PR_JSON" | jq -r '.data.author.login // .data.author.username // .data.issue.author.login // "N/A"')
    log_ok "PR #$pr_id: \"$PR_TITLE\" by @$PR_AUTHOR (state: $PR_STATE)"

    # Step 2: Get changed files
    log_step "Fetching changed files..."
    FILES_JSON=$(gl_run pr +files --owner "$OWNER" --repo "$REPO" --id "$pr_id")
    FILE_COUNT=0
    if echo "$FILES_JSON" | jq empty 2>/dev/null; then
        FILE_COUNT=$(echo "$FILES_JSON" | jq '.data.files | length' 2>/dev/null || echo "0")
    fi
    log_ok "Changed files: $FILE_COUNT"

    # List changed files
    if [[ "$FILE_COUNT" -gt 0 ]] && [[ "$FILE_COUNT" != "null" ]]; then
        for i in $(seq 0 $((FILE_COUNT - 1))); do
            FNAME=$(echo "$FILES_JSON" | jq -r ".data.files[$i].name // .data.files[$i].filename // \"unknown\"" 2>/dev/null || echo "unknown")
            echo "    $FNAME"
        done
    fi

    # Step 3: Get diff (extract file names and content from diff response)
    log_step "Fetching diff..."
    DIFF_JSON=$(gl_run pr +diff --owner "$OWNER" --repo "$REPO" --id "$pr_id")
    DIFF_CONTENT=""
    if echo "$DIFF_JSON" | jq empty 2>/dev/null; then
        DIFF_CONTENT=$(echo "$DIFF_JSON" | jq -r '
            [.data.files[]?.sections[]?.lines[]?.content // empty] | join("\n")
        ' 2>/dev/null | head -c 5000 || true)
    fi
    DIFF_LINES=$(echo "$DIFF_CONTENT" | wc -l)
    log_ok "Diff: $DIFF_LINES lines"

    # Step 4: AI-powered code review
    log_step "AI analyzing code quality..."

    # Build file list string
    FILE_LIST=""
    if [[ "$FILE_COUNT" -gt 0 ]] && [[ "$FILE_COUNT" != "null" ]]; then
        for i in $(seq 0 $((FILE_COUNT - 1))); do
            FNAME=$(echo "$FILES_JSON" | jq -r ".data.files[$i].name // .data.files[$i].filename // \"unknown\"" 2>/dev/null || echo "unknown")
            FILE_LIST+="- $FNAME"$'\n'
        done
    fi

    # Truncate diff to fit within context limits
    DIFF_TRUNCATED=$(echo "$DIFF_CONTENT" | head -c 4000)

    # ── Load gitlink-code-review skill (concise version) ──────────
    SKILL_DIR="$SCRIPT_DIR/../skills/gitlink-code-review"
    SKILL_DIMENSIONS=""
    if [[ -f "$SKILL_DIR/SKILL.md" ]]; then
        SKILL_DIMENSIONS=$(sed -n '/^## 📊 审查维度/,/^## 🔧 使用方式/p' "$SKILL_DIR/SKILL.md" | grep '^\- \*\*' | head -20)
    fi

    REVIEW_PROMPT="你是代码审查专家。请按 gitlink-code-review skill 的审查维度分析以下 PR。

## 审查维度与检查项

${SKILL_DIMENSIONS:-1. 代码质量: 复杂度、命名、注释、格式
2. 安全性: SQL注入、XSS、敏感信息、认证、输入验证
3. 性能: 循环效率、资源泄漏、N+1查询、内存
4. 可维护性: 代码重复、职责单一、依赖耦合、测试覆盖}

## 评分标准
- 90-100: 优秀，可直接合并
- 75-89: 良好，建议合并
- 60-74: 一般，需要改进
- <60: 较差，不建议合并

## 问题严重级别
- CRITICAL: 阻止合并
- HIGH: 强烈建议修复
- MEDIUM: 建议修复
- LOW: 可选修复

## PR 数据

PR 标题: $PR_TITLE
变更文件:
$FILE_LIST
代码差异:
$DIFF_TRUNCATED

## 输出要求

请严格按以下 JSON 格式输出，不要输出其他内容:
{\"total\": <0-100>, \"quality\": <0-25>, \"security\": <0-25>, \"performance\": <0-25>, \"maintainability\": <0-25>, \"issues\": [{\"severity\": \"HIGH/MEDIUM/LOW\", \"category\": \"quality/security/performance/maintainability\", \"file\": \"文件路径\", \"rule\": \"规则名\", \"description\": \"问题描述\", \"suggestion\": \"修复建议\"}], \"positive_notes\": [{\"description\": \"优秀实践描述\"}], \"recommendations\": [\"改进建议1\"], \"verdict\": \"PASS或FAIL\"}"

    # Call Claude Code CLI for AI review
    AI_AVAILABLE=false
    if command -v claude &>/dev/null; then
        log_info "Calling AI agent for code review (may take 30-60s)..."
        PROMPT_FILE=$(mktemp)
        AI_OUT_FILE=$(mktemp)
        echo "$REVIEW_PROMPT" > "$PROMPT_FILE"

        # Ensure CLAUDE_CODE_GIT_BASH_PATH is set for Windows
        if [[ -z "${CLAUDE_CODE_GIT_BASH_PATH:-}" ]] && command -v cygpath &>/dev/null; then
            export CLAUDE_CODE_GIT_BASH_PATH="$(cygpath -w "$(which bash)")"
        fi

        # Run claude in a subshell to isolate from set -euo pipefail
        # NOTE: must use pipe (not file redirect) for claude -p on Windows
        AI_EXIT=0
        (
            cat "$PROMPT_FILE" | timeout 300 claude -p --output-format json > "$AI_OUT_FILE" 2>/dev/null
        ) || AI_EXIT=$?

        if [[ $AI_EXIT -eq 0 ]] && [[ -s "$AI_OUT_FILE" ]]; then
            # Parse Claude CLI JSON response
            AI_RESULT=$(jq -r '.result // empty' "$AI_OUT_FILE" 2>/dev/null)
        else
            log_warn "AI call failed (exit: $AI_EXIT), falling back to keyword-based"
            AI_RESULT=""
        fi
        rm -f "$PROMPT_FILE" "$AI_OUT_FILE"

        if [[ -n "$AI_RESULT" ]]; then
            # Extract JSON block from AI response (may contain markdown wrapping)
            # Use python for reliable JSON extraction from mixed content
            AI_JSON=""
            if command -v python3 &>/dev/null; then
                AI_JSON=$(python3 -c "
import sys, json
text = sys.stdin.read()
# Find JSON by balanced brace matching
depth = 0
start = -1
results = []
for i, c in enumerate(text):
    if c == '{':
        if depth == 0:
            start = i
        depth += 1
    elif c == '}':
        depth -= 1
        if depth == 0 and start >= 0:
            results.append(text[start:i+1])
            start = -1
for m in reversed(results):
    try:
        obj = json.loads(m)
        if 'total' in obj and 'verdict' in obj:
            print(json.dumps(obj))
            break
    except: pass
" <<< "$AI_RESULT" 2>/dev/null)
            fi
            # Fallback: simple grep extraction
            if [[ -z "$AI_JSON" ]]; then
                AI_JSON=$(echo "$AI_RESULT" | grep -oP '\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}' | tail -1)
            fi

            if [[ -n "$AI_JSON" ]] && echo "$AI_JSON" | jq empty 2>/dev/null; then
                TOTAL_SCORE=$(echo "$AI_JSON" | jq -r '.total // 0' 2>/dev/null)
                SCORE_QUALITY=$(echo "$AI_JSON" | jq -r '.quality // 0' 2>/dev/null)
                SCORE_SECURITY=$(echo "$AI_JSON" | jq -r '.security // 0' 2>/dev/null)
                SCORE_PERFORMANCE=$(echo "$AI_JSON" | jq -r '.performance // 0' 2>/dev/null)
                SCORE_MAINTAINABILITY=$(echo "$AI_JSON" | jq -r '.maintainability // 0' 2>/dev/null)
                AI_VERDICT=$(echo "$AI_JSON" | jq -r '.verdict // "PASS"' 2>/dev/null)

                # Extract structured issues array (objects with severity/category/description)
                ISSUES_FOUND=()
                ISSUE_COUNT=$(echo "$AI_JSON" | jq '.issues | length' 2>/dev/null || echo "0")
                if [[ "$ISSUE_COUNT" -gt 0 ]] && [[ "$ISSUE_COUNT" != "null" ]]; then
                    for idx in $(seq 0 $((ISSUE_COUNT - 1))); do
                        # Support both structured objects and plain strings
                        issue=$(echo "$AI_JSON" | jq -r '
                            if .issues['"$idx"'] | type == "object" then
                                "[" + (.issues['"$idx"'].severity // "?") + "] " +
                                (.issues['"$idx"'].category // "?") + ": " +
                                (.issues['"$idx"'].description // .issues['"$idx"'].rule // "unknown") +
                                (if .issues['"$idx"'].file then " (" + .issues['"$idx"'].file + ")" else "" end) +
                                (if .issues['"$idx"'].suggestion then " → " + .issues['"$idx"'].suggestion else "" end)
                            else
                                .issues['"$idx"'] // empty
                            end
                        ' 2>/dev/null)
                        [[ -n "$issue" ]] && ISSUES_FOUND+=("$issue")
                    done
                fi

                # Extract positive notes and recommendations
                AI_POSITIVE=()
                POS_COUNT=$(echo "$AI_JSON" | jq '.positive_notes | length' 2>/dev/null || echo "0")
                if [[ "$POS_COUNT" -gt 0 ]] && [[ "$POS_COUNT" != "null" ]]; then
                    for idx in $(seq 0 $((POS_COUNT - 1))); do
                        note=$(echo "$AI_JSON" | jq -r '.positive_notes['"$idx"'].description // empty' 2>/dev/null)
                        [[ -n "$note" ]] && AI_POSITIVE+=("$note")
                    done
                fi

                AI_RECOMMENDATIONS=()
                REC_COUNT=$(echo "$AI_JSON" | jq '.recommendations | length' 2>/dev/null || echo "0")
                if [[ "$REC_COUNT" -gt 0 ]] && [[ "$REC_COUNT" != "null" ]]; then
                    for idx in $(seq 0 $((REC_COUNT - 1))); do
                        rec=$(echo "$AI_JSON" | jq -r '.recommendations['"$idx"'] // empty' 2>/dev/null)
                        [[ -n "$rec" ]] && AI_RECOMMENDATIONS+=("$rec")
                    done
                fi

                AI_AVAILABLE=true
                log_ok "AI review complete (verdict: $AI_VERDICT)"
            else
                log_warn "Could not parse AI response JSON, falling back to keyword-based"
            fi
        fi
    fi

    # Fallback: keyword-based heuristics if AI is not available
    if [[ "$AI_AVAILABLE" != "true" ]]; then
        log_warn "AI not available, falling back to keyword-based analysis"

        SCORE_QUALITY=25
        SCORE_SECURITY=25
        SCORE_PERFORMANCE=25
        SCORE_MAINTAINABILITY=25
        ISSUES_FOUND=()

        if echo "$DIFF_CONTENT" | grep -qiE 'password|secret|token|api_key|apikey|private_key'; then
            SCORE_SECURITY=$((SCORE_SECURITY - 15))
            ISSUES_FOUND+=("SECURITY: 检测到可能的硬编码凭证")
        fi
        if echo "$DIFF_CONTENT" | grep -qiE 'eval\(|exec\(|system\(|shell_exec|os\.system|subprocess\.call'; then
            SCORE_SECURITY=$((SCORE_SECURITY - 10))
            ISSUES_FOUND+=("SECURITY: 检测到危险函数调用")
        fi
        if echo "$DIFF_CONTENT" | grep -qiE 'TODO|FIXME|HACK|XXX'; then
            SCORE_QUALITY=$((SCORE_QUALITY - 5))
            ISSUES_FOUND+=("QUALITY: 存在 TODO/FIXME/HACK 注释")
        fi
        if echo "$DIFF_CONTENT" | grep -qiE 'SELECT \*|\.findAll\(\)|\.all\(\)'; then
            SCORE_PERFORMANCE=$((SCORE_PERFORMANCE - 10))
            ISSUES_FOUND+=("PERFORMANCE: 可能的全表查询")
        fi
        if echo "$DIFF_CONTENT" | grep -qiE 'sleep\(|time\.sleep|Thread\.sleep'; then
            SCORE_PERFORMANCE=$((SCORE_PERFORMANCE - 5))
            ISSUES_FOUND+=("PERFORMANCE: 检测到阻塞式 sleep")
        fi
        if [[ "$FILE_COUNT" -gt 20 ]]; then
            SCORE_MAINTAINABILITY=$((SCORE_MAINTAINABILITY - 10))
            ISSUES_FOUND+=("MAINTAINABILITY: 变更文件数量过多 ($FILE_COUNT)")
        fi

        TOTAL_SCORE=$((SCORE_QUALITY + SCORE_SECURITY + SCORE_PERFORMANCE + SCORE_MAINTAINABILITY))
        TOTAL_SCORE=$((TOTAL_SCORE < 0 ? 0 : TOTAL_SCORE))
    fi

    # Print review report
    divider
    if [[ "$AI_AVAILABLE" == "true" ]]; then
        log_info "AI Review Report for PR #$pr_id"
    else
        log_info "Review Report for PR #$pr_id (keyword-based)"
    fi
    echo ""
    echo "  Overall Score:        $TOTAL_SCORE / 100"
    echo "  Code Quality:         $SCORE_QUALITY / 25"
    echo "  Security:             $SCORE_SECURITY / 25"
    echo "  Performance:          $SCORE_PERFORMANCE / 25"
    echo "  Maintainability:      $SCORE_MAINTAINABILITY / 25"
    echo ""

    if [[ ${#ISSUES_FOUND[@]} -gt 0 ]]; then
        echo "  Issues Found:"
        for issue in "${ISSUES_FOUND[@]}"; do
            echo "    - $issue"
        done
        echo ""
    fi

    if [[ "${#AI_POSITIVE[@]}" -gt 0 ]]; then
        echo "  Positive Notes:"
        for note in "${AI_POSITIVE[@]}"; do
            echo "    + $note"
        done
        echo ""
    fi

    if [[ "${#AI_RECOMMENDATIONS[@]}" -gt 0 ]]; then
        echo "  Recommendations:"
        for rec in "${AI_RECOMMENDATIONS[@]}"; do
            echo "    > $rec"
        done
        echo ""
    fi

    # Step 5: Post review comment
    if [[ "$AI_AVAILABLE" == "true" ]]; then
        REVIEW_HEADER="## AI Code Quality Review - PR #$pr_id"
    else
        REVIEW_HEADER="## Code Quality Review - PR #$pr_id (keyword-based)"
    fi

    REVIEW_BODY="$REVIEW_HEADER

### Scores
| Dimension | Score | Max |
|-----------|-------|-----|
| Code Quality | $SCORE_QUALITY | 25 |
| Security | $SCORE_SECURITY | 25 |
| Performance | $SCORE_PERFORMANCE | 25 |
| Maintainability | $SCORE_MAINTAINABILITY | 25 |
| **Total** | **$TOTAL_SCORE** | **100** |

### Issues Found"

    if [[ ${#ISSUES_FOUND[@]} -gt 0 ]]; then
        for issue in "${ISSUES_FOUND[@]}"; do
            REVIEW_BODY+=$'\n'"- $issue"
        done
    else
        REVIEW_BODY+=$'\n'"No issues found."
    fi

    if [[ "${#AI_POSITIVE[@]}" -gt 0 ]]; then
        REVIEW_BODY+=$'\n'$'\n'"### Positive Notes"
        for note in "${AI_POSITIVE[@]}"; do
            REVIEW_BODY+=$'\n'"- $note"
        done
    fi

    if [[ "${#AI_RECOMMENDATIONS[@]}" -gt 0 ]]; then
        REVIEW_BODY+=$'\n'$'\n'"### Recommendations"
        for rec in "${AI_RECOMMENDATIONS[@]}"; do
            REVIEW_BODY+=$'\n'"- $rec"
        done
    fi

    REVIEW_BODY+="

### Verdict
$(if [[ $TOTAL_SCORE -ge $THRESHOLD ]]; then echo "**PASS** - Score $TOTAL_SCORE >= threshold $THRESHOLD. Ready to merge."; else echo "**FAIL** - Score $TOTAL_SCORE < threshold $THRESHOLD. Please address the issues above."; fi)

---
*Auto-reviewed by gitlink-cli code-quality-gatekeeper workflow (skill: gitlink-code-review)*"

    log_step "Posting review comment..."
    REVIEW_STATE=$(if [[ $TOTAL_SCORE -ge $THRESHOLD ]]; then echo "approved"; else echo "commented"; fi)
    REVIEW_JSON=$(jq -n --arg body "$REVIEW_BODY" --arg state "$REVIEW_STATE" \
        '{body: $body, state: $state}')
    REVIEW_RESULT=$(gl_run api POST "/$OWNER/$REPO/pulls/$pr_id/reviews" \
        --body "$REVIEW_JSON" 2>&1) || true

    if [[ "$(json_ok "$REVIEW_RESULT")" == "true" ]]; then
        log_ok "Review posted"
    else
        log_warn "Review post may have failed (review API might not be available)"
    fi

    # Step 6: Check CI status (API may not be available)
    log_step "Checking CI build status..."
    CI_JSON=$(gl_run ci +builds --owner "$OWNER" --repo "$REPO")
    CI_COUNT=$(echo "$CI_JSON" | jq '.data.builds // .data | length' 2>/dev/null || echo "0")
    CI_PASSED=true

    if [[ "$CI_COUNT" -gt 0 ]] && [[ "$CI_COUNT" != "null" ]]; then
        for i in $(seq 0 $((CI_COUNT - 1))); do
            CI_STATUS=$(echo "$CI_JSON" | jq -r ".data.builds[$i].status // .data.builds[$i].state // .data[$i].status // .data[$i].state // \"unknown\"")
            CI_NAME=$(echo "$CI_JSON" | jq -r ".data.builds[$i].name // .data[$i].name // \"build\"")
            if [[ "$CI_STATUS" != "success" && "$CI_STATUS" != "passed" && "$CI_STATUS" != "completed" ]]; then
                CI_PASSED=false
                log_warn "CI '$CI_NAME' status: $CI_STATUS"
            else
                log_ok "CI '$CI_NAME' status: $CI_STATUS"
            fi
        done
    else
        log_info "No CI builds found"
    fi

    # Step 7: Auto-merge if quality passes
    if [[ $TOTAL_SCORE -ge $THRESHOLD && "$CI_PASSED" == "true" ]]; then
        log_step "Quality score $TOTAL_SCORE >= $THRESHOLD and CI passed"
        if [[ "$DRY_RUN" == "true" ]]; then
            log_warn "[DRY RUN] Would auto-merge PR #$pr_id"
        else
            log_step "Auto-merging PR #$pr_id..."
            MERGE_RESULT=$(gl_run pr +merge --owner "$OWNER" --repo "$REPO" --id "$pr_id" --method merge 2>&1) || true
            if [[ "$(json_ok "$MERGE_RESULT")" == "true" ]]; then
                log_ok "PR #$pr_id merged successfully!"
            else
                log_err "Auto-merge failed: $(json_error "$MERGE_RESULT")"
            fi
        fi
    else
        log_warn "PR #$pr_id not auto-merged (score: $TOTAL_SCORE, threshold: $THRESHOLD, CI passed: $CI_PASSED)"
    fi

    echo ""
    return 0
}

# ── Main ─────────────────────────────────────────────────────────────
log_title "Code Quality Gatekeeper"

if [[ -n "$PR_ID" ]]; then
    # Review specific PR
    review_pr "$PR_ID"
else
    # Review all open PRs
    log_step "Fetching open PRs..."
    PRS_JSON=$(gl_check pr +list --owner "$OWNER" --repo "$REPO" --state open --limit 50)
    PR_COUNT=$(echo "$PRS_JSON" | jq '(.data.issues // .data.pulls // .data | if type == "array" then . else [] end) | length')
    log_ok "Found $PR_COUNT open PRs"

    if [[ "$PR_COUNT" -eq 0 ]]; then
        log_info "No open PRs to review"
        exit 0
    fi

    REVIEWED=0
    PASSED=0
    FAILED=0

    PR_DATA_PATH='(.data.issues // .data.pulls // .data | if type == "array" then . else [] end)'
    for i in $(seq 0 $((PR_COUNT - 1))); do
        pid=$(echo "$PRS_JSON" | jq -r "$PR_DATA_PATH[$i].pull_request_number // $PR_DATA_PATH[$i].number // $PR_DATA_PATH[$i].id // empty")
        [[ -z "$pid" ]] && continue
        review_pr "$pid"
        ((REVIEWED++))
    done

    log_title "Gatekeeper Summary"
    echo "  PRs Reviewed: $REVIEWED"
    echo "  Threshold:    $THRESHOLD"
fi
