#!/bin/bash
# verify_codegen.sh - Verifies that generated files are synchronized with source definitions
# Usage: ./scripts/verify_codegen.sh
# Exit code: 0 if sync, 1 if stale files detected

set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

echo "🔍 Verifying generated files are synchronized..."

# Create a backup of current generated files
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT

# List of generated files to check
GENERATED_FILES=(
    "shared/pkg/constants/z_generated_tables.go"
    "shared/pkg/constants/z_generated_fields.go"
    "shared/pkg/constants/z_generated_columns.go"
    "shared/pkg/models/z_generated.go"
    "frontend/src/generated-schema.ts"
    "frontend/src/generated-error-codes.ts"
    "frontend/src/generated-api.ts"
)

# Backup current files
echo "📦 Backing up current generated files..."
for file in "${GENERATED_FILES[@]}"; do
    full_path="$PROJECT_ROOT/$file"
    if [[ -f "$full_path" ]]; then
        backup_dir="$TEMP_DIR/$(dirname "$file")"
        mkdir -p "$backup_dir"
        cp "$full_path" "$TEMP_DIR/$file"
    fi
done

# Run codegen
echo "🏗️  Running code generation..."
cd "$PROJECT_ROOT/backend"
go run ./cmd/codegen 2>&1 | grep -v "^📖\|^📊\|^✅\|^🎉" || true
cd "$PROJECT_ROOT"

# Compare files
echo "🔬 Checking for differences..."
CHANGED_FILES=()
for file in "${GENERATED_FILES[@]}"; do
    full_path="$PROJECT_ROOT/$file"
    backup_path="$TEMP_DIR/$file"
    
    if [[ -f "$full_path" && -f "$backup_path" ]]; then
        if ! diff -q "$backup_path" "$full_path" > /dev/null 2>&1; then
            CHANGED_FILES+=("$file")
        fi
    elif [[ -f "$full_path" && ! -f "$backup_path" ]]; then
        # New file was generated
        CHANGED_FILES+=("$file (NEW)")
    fi
done

# Restore original files (we only wanted to check, not modify)
echo "🔄 Restoring original files..."
for file in "${GENERATED_FILES[@]}"; do
    backup_path="$TEMP_DIR/$file"
    full_path="$PROJECT_ROOT/$file"
    if [[ -f "$backup_path" ]]; then
        cp "$backup_path" "$full_path"
    fi
done

# Report results
CODEGEN_FAILED=0
if [[ ${#CHANGED_FILES[@]} -eq 0 ]]; then
    echo "✅ All generated files are synchronized!"
else
    echo ""
    echo "❌ ERROR: Generated files are STALE!"
    echo "The following files would change if codegen is run:"
    echo ""
    for file in "${CHANGED_FILES[@]}"; do
        echo "  - $file"
    done
    echo ""
    echo "Run 'cd backend && go run ./cmd/codegen/' to regenerate."
    CODEGEN_FAILED=1
fi

# ─────────────────────────────────────────────────────────────────────────────
# Check 2: Raw _System_ string literals in frontend source
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "🔍 Checking for raw '_System_' string literals in frontend source..."

# Grep for raw '_System_' strings in quotes or template literals,
# excluding generated files and codegen-exempt lines.
# Patterns caught: '_System_...', "_System_...", `_System_...`
RAW_SYSTEM_HITS=$(grep -rn "_System_" "$PROJECT_ROOT/frontend/src/" \
    --include='*.ts' --include='*.tsx' \
    | grep -v 'generated-schema\.ts' \
    | grep -v 'generated-error-codes\.ts' \
    | grep -v 'generated-api\.ts' \
    | grep -v 'SchemaDefinitions\.ts' \
    | grep -v '// codegen-exempt' \
    | grep -v '// eslint-disable' \
    | grep -E "'_System_|\"_System_|\`_System_" \
    || true)

if [[ -z "$RAW_SYSTEM_HITS" ]]; then
    echo "✅ No raw '_System_' string literals found!"
else
    echo ""
    echo "❌ ERROR: Raw '_System_' string literals found in frontend source!"
    echo "Use SYSTEM_TABLE_NAMES.* or FIELDS_SYSTEM_*.* from generated-schema.ts instead."
    echo ""
    echo "$RAW_SYSTEM_HITS"
    echo ""
    echo "If intentional, add '// codegen-exempt' comment to the line."
    CODEGEN_FAILED=1
fi

# ─────────────────────────────────────────────────────────────────────────────
# Check 3: Hardcoded objectApiName string literals
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "🔍 Checking for hardcoded objectApiName string literals..."

# Pattern: objectApiName: 'SomeTable' (string literal instead of SYSTEM_TABLE_NAMES.*)
OBJ_API_HITS=$(grep -rn "objectApiName: '" "$PROJECT_ROOT/frontend/src/" \
    --include='*.ts' --include='*.tsx' \
    | grep -v '\.test\.' \
    | grep -v '\.spec\.' \
    | grep -v '// codegen-exempt' \
    | grep -v '//.*objectApiName' \
    || true)

# Also catch RECORDS('TableName') pattern
RECORDS_HITS=$(grep -rn "RECORDS('" "$PROJECT_ROOT/frontend/src/" \
    --include='*.ts' --include='*.tsx' \
    | grep -v '\.test\.' \
    | grep -v '\.spec\.' \
    | grep -v '// codegen-exempt' \
    || true)

COMBINED_HITS=""
if [[ -n "$OBJ_API_HITS" ]]; then COMBINED_HITS="$OBJ_API_HITS"; fi
if [[ -n "$RECORDS_HITS" ]]; then
    if [[ -n "$COMBINED_HITS" ]]; then
        COMBINED_HITS="$COMBINED_HITS"$'\n'"$RECORDS_HITS"
    else
        COMBINED_HITS="$RECORDS_HITS"
    fi
fi

if [[ -z "$COMBINED_HITS" ]]; then
    echo "✅ No hardcoded objectApiName string literals found!"
else
    echo ""
    echo "❌ ERROR: Hardcoded table name strings found in frontend source!"
    echo "Use SYSTEM_TABLE_NAMES.* from generated-schema.ts instead of raw strings."
    echo ""
    echo "$COMBINED_HITS"
    echo ""
    echo "If intentional, add '// codegen-exempt' comment to the line."
    CODEGEN_FAILED=1
fi

# ─────────────────────────────────────────────────────────────────────────────
# Check 4: Direct apiClient imports outside infrastructure layer
#
# Feature, page, and hook code must use named API modules (approvalsAPI,
# dataAPI, feedAPI, processBuilderAPI, etc.) and consume isSalesforceMode
# via useRuntime(). Importing apiClient directly is a layer violation.
#
# Allowed: src/infrastructure/api/**   (the layer that owns apiClient)
#          src/contexts/RuntimeContext.tsx  (surfaces isSalesforceMode)
#
# To exempt a specific line: add '// arch-exempt' comment with justification.
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "🔍 Checking for direct apiClient imports outside infrastructure layer..."

API_CLIENT_HITS=$(grep -rn "import.*apiClient.*from" "$PROJECT_ROOT/frontend/src/" \
    --include='*.ts' --include='*.tsx' \
    | grep -v "infrastructure/api/" \
    | grep -v "contexts/RuntimeContext.tsx" \
    | grep -v "// arch-exempt" \
    || true)

if [[ -z "$API_CLIENT_HITS" ]]; then
    echo "✅ No direct apiClient imports found outside infrastructure layer!"
else
    echo ""
    echo "❌ ERROR: Direct apiClient import(s) found outside src/infrastructure/api/!"
    echo ""
    echo "Feature/page/hook code must use named API modules:"
    echo "  dataAPI, approvalsAPI, processBuilderAPI, feedAPI, metadataAPI, etc."
    echo ""
    echo "For isSalesforceMode checks in React components, use:"
    echo "  const { isSalesforceMode } = useRuntime();"
    echo ""
    echo "Violations:"
    echo "$API_CLIENT_HITS"
    echo ""
    echo "If genuinely unavoidable (non-React imperative code), add '// arch-exempt'"
    echo "comment with a justification on the import line."
    CODEGEN_FAILED=1
fi

# ─────────────────────────────────────────────────────────────────────────────
# Check 5: Direct localStorage access for session state outside client.ts
#
# Auth token and instance_url are managed exclusively by APIClient.
# Any direct localStorage.getItem/setItem for these keys outside client.ts
# is a session-state SSoT violation that bypasses the auth layer.
#
# Allowed:
#   - src/infrastructure/api/client.ts  (owns these keys)
#
# Known legitimate exception: reading instance_url for building Salesforce
# debug URLs (not mode detection) → annotate with '// arch-exempt'
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "🔍 Checking for localStorage session key access outside API client..."

SESSION_STORAGE_HITS=$(grep -rn \
    "localStorage\.\(getItem\|setItem\|removeItem\).*['\"]instance_url\|localStorage\.\(getItem\|setItem\|removeItem\).*['\"]token\|localStorage\.\(getItem\|setItem\|removeItem\).*['\"]auth" \
    "$PROJECT_ROOT/frontend/src/" \
    --include='*.ts' --include='*.tsx' \
    | grep -v "infrastructure/api/client\.ts" \
    | grep -v "// arch-exempt" \
    || true)

if [[ -z "$SESSION_STORAGE_HITS" ]]; then
    echo "✅ No direct localStorage session key access outside API client!"
else
    echo ""
    echo "❌ ERROR: Direct localStorage access for session keys outside infrastructure/api/client.ts!"
    echo ""
    echo "Session state (token, instance_url) is owned by APIClient."
    echo "Use apiClient.setSession() / apiClient.clearSession() / apiClient.isSalesforceMode()."
    echo "For mode checks in React components, use: const { isSalesforceMode } = useRuntime();"
    echo ""
    echo "Violations:"
    echo "$SESSION_STORAGE_HITS"
    echo ""
    echo "If genuinely unavoidable (e.g. building external URLs), add '// arch-exempt'."
    CODEGEN_FAILED=1
fi

# ─────────────────────────────────────────────────────────────────────────────
# Check 6: Hardcoded processType string literals in navigate() state
#
# PROCESS_TYPE.APPROVAL / PROCESS_TYPE.AUTO_LAUNCHED_FLOW etc. are the SSoT
# for process type values. Hardcoding 'Approval' or 'Flow' in navigate()
# state or location.state reads creates divergence risk.
#
# Exception: JSX labels/text, option values in <select>, or comments.
# To exempt a specific line: add '// codegen-exempt' comment.
# ─────────────────────────────────────────────────────────────────────────────
echo ""
echo "🔍 Checking for hardcoded processType string literals in navigate state..."

PROCESS_TYPE_HITS=$(grep -rn "processType: ['\"]" "$PROJECT_ROOT/frontend/src/" \
    --include='*.ts' --include='*.tsx' \
    | grep -v "// codegen-exempt" \
    | grep -v "// arch-exempt" \
    | grep -v "\.test\." \
    | grep -v "\.spec\." \
    || true)

if [[ -z "$PROCESS_TYPE_HITS" ]]; then
    echo "✅ No hardcoded processType string literals found!"
else
    echo ""
    echo "❌ ERROR: Hardcoded processType string literals found!"
    echo ""
    echo "Use PROCESS_TYPE.* constants from core/constants/FlowConstants.ts:"
    echo "  import { PROCESS_TYPE } from '../../core/constants/FlowConstants';"
    echo "  navigate('/path', { state: { processType: PROCESS_TYPE.APPROVAL } })"
    echo ""
    echo "Violations:"
    echo "$PROCESS_TYPE_HITS"
    echo ""
    echo "If intentional (e.g. display labels), add '// codegen-exempt'."
    CODEGEN_FAILED=1
fi

# ═══════════════════════════════════════════════════
# Check 7: Frontend unit tests (vitest)
# ═══════════════════════════════════════════════════
echo ""
echo "🔍 Running frontend unit tests..."

if ! (cd "$PROJECT_ROOT/frontend" && npx vitest run --reporter=dot 2>&1 | tail -3); then
    echo ""
    echo "❌ ERROR: Frontend unit tests failed!"
    echo "Run 'cd frontend && npx vitest run' to see full output."
    CODEGEN_FAILED=1
else
    echo "✅ Frontend unit tests passed!"
fi

# ═══════════════════════════════════════════════════
# Check 8: Backend unit tests (go test)
# ═══════════════════════════════════════════════════
echo ""
echo "🔍 Running backend unit tests..."

if ! (cd "$PROJECT_ROOT/backend" && go test ./... 2>&1 | tail -5); then
    echo ""
    echo "❌ ERROR: Backend unit tests failed!"
    echo "Run 'cd backend && go test ./...' to see full output."
    CODEGEN_FAILED=1
else
    echo "✅ Backend unit tests passed!"
fi

# Final exit
if [[ $CODEGEN_FAILED -eq 1 ]]; then
    exit 1
fi
exit 0
