#!/bin/bash
# MNW CLI — Internal API endpoint test suite
#
# Tests all TUI-backing endpoints against a live MNW server.
# Uses ffmpeg to generate test audio content for upload tests.
#
# Usage:
#   ./tests/api_endpoints.sh                    # Run against localhost:3000
#   MNW_URL=http://host:3000 ./tests/api_endpoints.sh  # Custom server
#
# Prerequisites:
#   - MNW server running with CLI_SERVICE_TOKEN set
#   - ffmpeg (for generating test audio)
#   - curl, jq
#   - Seed data loaded (elena user + demo projects)

set -euo pipefail

# ── Configuration ──────────────────────────────────────────────────
MNW_URL="${MNW_URL:-http://localhost:3000}"
TOKEN="${MNW_SERVICE_TOKEN:-7f28a67c2e97a1b774aa482692c2a7d3e011da6de625a9a8b480d9156512e050}"
ELENA_ID="11111111-1111-1111-1111-111111111111"
PROJECT_NEWSLETTER="22222222-2222-2222-2222-222222222221"
PROJECT_PODCAST="22222222-2222-2222-2222-222222222222"
ITEM_ESSAY="33333333-3333-3333-3333-333333333331"
ITEM_PODCAST="33333333-3333-3333-3333-333333333341"

TMPDIR="$(mktemp -d)"
trap 'rm -rf "$TMPDIR"' EXIT

PASS=0
FAIL=0
SKIP=0
ERRORS=""

# ── Helpers ────────────────────────────────────────────────────────
AUTH="Authorization: Bearer $TOKEN"
CT="Content-Type: application/json"

api() {
    local method="$1" path="$2"
    shift 2
    curl -sf -X "$method" -H "$AUTH" -H "$CT" "$@" "${MNW_URL}${path}"
}

api_status() {
    local method="$1" path="$2"
    shift 2
    curl -s -o /dev/null -w '%{http_code}' -X "$method" -H "$AUTH" -H "$CT" "$@" "${MNW_URL}${path}"
}

pass() {
    PASS=$((PASS + 1))
    printf "  [PASS] %s\n" "$1"
}

fail() {
    FAIL=$((FAIL + 1))
    printf "  [FAIL] %s — %s\n" "$1" "$2"
    ERRORS="${ERRORS}\n  - $1: $2"
}

skip() {
    SKIP=$((SKIP + 1))
    printf "  [SKIP] %s — %s\n" "$1" "$2"
}

assert_status() {
    local name="$1" expected="$2" method="$3" path="$4"
    shift 4
    local status
    status=$(curl -s -o /dev/null -w '%{http_code}' -X "$method" -H "$AUTH" -H "$CT" "$@" "${MNW_URL}${path}")
    if [ "$status" = "$expected" ]; then
        pass "$name (HTTP $status)"
    else
        fail "$name" "expected $expected, got $status"
    fi
}

assert_json_field() {
    local name="$1" json="$2" field="$3"
    local val
    val=$(echo "$json" | jq -r "$field" 2>/dev/null)
    if [ -n "$val" ] && [ "$val" != "null" ]; then
        pass "$name ($field=$val)"
    else
        fail "$name" "missing field $field"
    fi
}

assert_json_eq() {
    local name="$1" json="$2" field="$3" expected="$4"
    local val
    val=$(echo "$json" | jq -r "$field" 2>/dev/null)
    if [ "$val" = "$expected" ]; then
        pass "$name"
    else
        fail "$name" "expected $field=$expected, got $val"
    fi
}

assert_json_count() {
    local name="$1" json="$2" min="$3"
    local count
    count=$(echo "$json" | jq 'length' 2>/dev/null)
    if [ "$count" -ge "$min" ]; then
        pass "$name (count=$count)"
    else
        fail "$name" "expected >= $min items, got $count"
    fi
}

# ── Check prerequisites ───────────────────────────────────────────
echo "=== MNW CLI API Endpoint Tests ==="
echo "Server: $MNW_URL"
echo ""

# Verify server is up
if ! curl -sf "${MNW_URL}/api/health" > /dev/null 2>&1; then
    echo "ERROR: MNW server not reachable at $MNW_URL"
    exit 1
fi
echo "Server reachable."

# Verify ffmpeg
if ! command -v ffmpeg > /dev/null 2>&1; then
    echo "WARNING: ffmpeg not found — upload tests will be skipped"
    HAS_FFMPEG=false
else
    HAS_FFMPEG=true
fi

# Verify auth works
STATUS=$(api_status GET "/api/internal/creator/projects?user_id=$ELENA_ID")
if [ "$STATUS" != "200" ]; then
    echo "ERROR: Auth failed (HTTP $STATUS). Check CLI_SERVICE_TOKEN."
    exit 1
fi
echo "Auth OK."
echo ""

# ── Generate test content with ffmpeg ──────────────────────────────
if $HAS_FFMPEG; then
    echo "Generating test audio files..."
    # 5-second sine wave WAV (440 Hz)
    ffmpeg -y -f lavfi -i "sine=frequency=440:duration=5" \
        -ar 44100 -ac 2 "$TMPDIR/test_track.wav" 2>/dev/null
    echo "  Created test_track.wav ($(du -h "$TMPDIR/test_track.wav" | cut -f1))"

    # 3-second MP3 (220 Hz)
    ffmpeg -y -f lavfi -i "sine=frequency=220:duration=3" \
        -ar 44100 -ac 1 -b:a 128k "$TMPDIR/test_audio.mp3" 2>/dev/null
    echo "  Created test_audio.mp3 ($(du -h "$TMPDIR/test_audio.mp3" | cut -f1))"

    # Small ZIP file (placeholder download)
    echo "test content for zip" > "$TMPDIR/readme.txt"
    (cd "$TMPDIR" && zip -q test_download.zip readme.txt)
    echo "  Created test_download.zip ($(du -h "$TMPDIR/test_download.zip" | cut -f1))"
    echo ""
fi

# ══════════════════════════════════════════════════════════════════
# Section 1: Creator Projects & Stats
# ══════════════════════════════════════════════════════════════════
echo "── 1. Projects & Stats ──"

# List projects
PROJECTS=$(api GET "/api/internal/creator/projects?user_id=$ELENA_ID")
assert_json_count "List projects" "$PROJECTS" 3

# Check project fields
FIRST=$(echo "$PROJECTS" | jq '.[0]')
assert_json_field "Project has id" "$FIRST" ".id"
assert_json_field "Project has slug" "$FIRST" ".slug"
assert_json_field "Project has title" "$FIRST" ".title"
assert_json_field "Project has project_type" "$FIRST" ".project_type"

# List items for a project
ITEMS=$(api GET "/api/internal/creator/projects/$PROJECT_NEWSLETTER/items?user_id=$ELENA_ID")
assert_json_count "List newsletter items" "$ITEMS" 3

# Stats
STATS=$(api GET "/api/internal/creator/stats?user_id=$ELENA_ID&range=30d")
assert_json_field "Stats has total_items" "$STATS" ".total_items"
assert_json_field "Stats has total_projects" "$STATS" ".total_projects"
assert_json_field "Stats has current_revenue_cents" "$STATS" ".current_revenue_cents"

# Stats ranges
for range in 7d 30d 90d all; do
    assert_status "Stats range=$range" 200 GET "/api/internal/creator/stats?user_id=$ELENA_ID&range=$range"
done

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 2: Storage Info
# ══════════════════════════════════════════════════════════════════
echo "── 2. Storage ──"

STORAGE=$(api GET "/api/internal/creator/storage?user_id=$ELENA_ID")
assert_json_field "Storage has storage_used_bytes" "$STORAGE" ".storage_used_bytes"
assert_json_field "Storage has max_storage_bytes" "$STORAGE" ".max_storage_bytes"
assert_json_field "Storage has allows_file_uploads" "$STORAGE" ".allows_file_uploads"

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 3: Item Detail & Mutations
# ══════════════════════════════════════════════════════════════════
echo "── 3. Item Detail & Mutations ──"

# Get item detail
DETAIL=$(api GET "/api/internal/creator/items/$ITEM_ESSAY?user_id=$ELENA_ID")
assert_json_eq "Item detail title" "$DETAIL" ".title" "The Art of Doing Less"
assert_json_field "Item has price_cents" "$DETAIL" ".price_cents"
assert_json_field "Item has item_type" "$DETAIL" ".item_type"
assert_json_field "Item has is_public" "$DETAIL" ".is_public"
assert_json_field "Item has created_at" "$DETAIL" ".created_at"

# Get item versions
VERSIONS=$(api GET "/api/internal/creator/items/$ITEM_ESSAY/versions?user_id=$ELENA_ID")
# May be empty, just verify it returns an array
VCOUNT=$(echo "$VERSIONS" | jq 'length' 2>/dev/null)
if [ "$VCOUNT" -ge 0 ] 2>/dev/null; then
    pass "Item versions returns array (count=$VCOUNT)"
else
    fail "Item versions" "did not return array"
fi

# Create a new item
NEW_ITEM=$(api POST "/api/internal/creator/items" \
    -d "{\"user_id\":\"$ELENA_ID\",\"project_id\":\"$PROJECT_NEWSLETTER\",\"title\":\"Test Item $(date +%s)\",\"item_type\":\"text\",\"price_cents\":999}")
NEW_ITEM_ID=$(echo "$NEW_ITEM" | jq -r '.item_id')
if [ -n "$NEW_ITEM_ID" ] && [ "$NEW_ITEM_ID" != "null" ]; then
    pass "Create item (id=$NEW_ITEM_ID)"
else
    fail "Create item" "no item_id in response"
    NEW_ITEM_ID=""
fi

# Update the item
if [ -n "$NEW_ITEM_ID" ]; then
    UPDATED=$(api PUT "/api/internal/creator/items/$NEW_ITEM_ID" \
        -d "{\"user_id\":\"$ELENA_ID\",\"title\":\"Updated Title\",\"description\":\"A test description\",\"price_cents\":1299}")
    assert_json_eq "Update item title" "$UPDATED" ".title" "Updated Title"
    assert_json_eq "Update item price" "$UPDATED" ".price_cents" "1299"

    # Unpublish / publish toggle
    UNPUB=$(api POST "/api/internal/creator/items/$NEW_ITEM_ID/unpublish" \
        -d "{\"user_id\":\"$ELENA_ID\"}")
    assert_json_eq "Unpublish item" "$UNPUB" ".is_public" "false"

    PUB=$(api POST "/api/internal/creator/items/$NEW_ITEM_ID/publish" \
        -d "{\"user_id\":\"$ELENA_ID\"}")
    assert_json_eq "Publish item" "$PUB" ".is_public" "true"
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 4: Blog Posts
# ══════════════════════════════════════════════════════════════════
echo "── 4. Blog Posts ──"

# List blog posts
BLOG=$(api GET "/api/internal/creator/projects/$PROJECT_NEWSLETTER/blog?user_id=$ELENA_ID")
BLOG_COUNT=$(echo "$BLOG" | jq 'length' 2>/dev/null)
if [ "$BLOG_COUNT" -ge 0 ] 2>/dev/null; then
    pass "List blog posts (count=$BLOG_COUNT)"
else
    fail "List blog posts" "did not return array"
fi

# Create blog post
TS=$(date +%s)
NEW_POST=$(api POST "/api/internal/creator/blog" \
    -d "{\"user_id\":\"$ELENA_ID\",\"project_id\":\"$PROJECT_NEWSLETTER\",\"title\":\"Test Post $TS\",\"body_markdown\":\"Hello from the test suite.\",\"publish\":false}")
POST_ID=$(echo "$NEW_POST" | jq -r '.id')
if [ -n "$POST_ID" ] && [ "$POST_ID" != "null" ]; then
    pass "Create blog post (id=$POST_ID)"
    assert_json_eq "Blog post title" "$NEW_POST" ".title" "Test Post $TS"
    assert_json_eq "Blog post unpublished" "$NEW_POST" ".is_published" "false"
else
    fail "Create blog post" "no id in response"
    POST_ID=""
fi

# Delete blog post
if [ -n "$POST_ID" ]; then
    assert_status "Delete blog post" 204 DELETE "/api/internal/creator/blog/$POST_ID?user_id=$ELENA_ID"
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 5: Promo Codes
# ══════════════════════════════════════════════════════════════════
echo "── 5. Promo Codes ──"

# List promo codes
PROMOS=$(api GET "/api/internal/creator/promo-codes?user_id=$ELENA_ID")
PROMO_COUNT=$(echo "$PROMOS" | jq 'length' 2>/dev/null)
if [ "$PROMO_COUNT" -ge 0 ] 2>/dev/null; then
    pass "List promo codes (count=$PROMO_COUNT)"
else
    fail "List promo codes" "did not return array"
fi

# Create promo code
PROMO_CODE="TEST$(date +%s)"
NEW_PROMO=$(api POST "/api/internal/creator/promo-codes" \
    -d "{\"user_id\":\"$ELENA_ID\",\"code\":\"$PROMO_CODE\",\"discount_type\":\"percentage\",\"discount_value\":25}")
PROMO_ID=$(echo "$NEW_PROMO" | jq -r '.id')
if [ -n "$PROMO_ID" ] && [ "$PROMO_ID" != "null" ]; then
    pass "Create promo code (code=$PROMO_CODE)"
    assert_json_eq "Promo discount" "$NEW_PROMO" ".discount_value" "25"
else
    fail "Create promo code" "no id in response"
    PROMO_ID=""
fi

# Delete promo code
if [ -n "$PROMO_ID" ]; then
    assert_status "Delete promo code" 204 DELETE "/api/internal/creator/promo-codes/$PROMO_ID?user_id=$ELENA_ID"
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 6: License Keys
# ══════════════════════════════════════════════════════════════════
echo "── 6. License Keys ──"

# List license keys for an item
KEYS=$(api GET "/api/internal/creator/items/$ITEM_ESSAY/keys?user_id=$ELENA_ID")
KEY_COUNT=$(echo "$KEYS" | jq 'length' 2>/dev/null)
if [ "$KEY_COUNT" -ge 0 ] 2>/dev/null; then
    pass "List license keys (count=$KEY_COUNT)"
else
    fail "List license keys" "did not return array"
fi

# Generate a license key
NEW_KEY=$(api POST "/api/internal/creator/items/$ITEM_ESSAY/keys" \
    -d "{\"user_id\":\"$ELENA_ID\"}")
KEY_ID=$(echo "$NEW_KEY" | jq -r '.id')
KEY_CODE=$(echo "$NEW_KEY" | jq -r '.key_code')
if [ -n "$KEY_ID" ] && [ "$KEY_ID" != "null" ]; then
    pass "Generate license key (code=$KEY_CODE)"
else
    fail "Generate license key" "no id in response"
    KEY_ID=""
fi

# Revoke license key
if [ -n "$KEY_ID" ]; then
    assert_status "Revoke license key" 204 POST "/api/internal/creator/keys/$KEY_ID/revoke" \
        -d "{\"user_id\":\"$ELENA_ID\"}"
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 7: Analytics & Transactions
# ══════════════════════════════════════════════════════════════════
echo "── 7. Analytics & Transactions ──"

# Analytics
for range in 7d 30d 90d all; do
    ANALYTICS=$(api GET "/api/internal/creator/analytics?user_id=$ELENA_ID&range=$range")
    assert_json_field "Analytics ($range) has buckets" "$ANALYTICS" ".buckets"
    assert_json_field "Analytics ($range) has current_revenue_cents" "$ANALYTICS" ".current_revenue_cents"
done

# Top projects in analytics
ANALYTICS_30=$(api GET "/api/internal/creator/analytics?user_id=$ELENA_ID&range=30d")
TOP=$(echo "$ANALYTICS_30" | jq '.top_projects' 2>/dev/null)
if [ "$(echo "$TOP" | jq 'type')" = '"array"' ]; then
    pass "Analytics top_projects is array"
else
    fail "Analytics top_projects" "not an array"
fi

# Transactions
TXS=$(api GET "/api/internal/creator/transactions?user_id=$ELENA_ID")
TX_COUNT=$(echo "$TXS" | jq 'length' 2>/dev/null)
if [ "$TX_COUNT" -ge 0 ] 2>/dev/null; then
    pass "List transactions (count=$TX_COUNT)"
else
    fail "List transactions" "did not return array"
fi

# Export sales CSV
EXPORT=$(api GET "/api/internal/creator/export/sales?user_id=$ELENA_ID")
assert_json_field "Export has csv" "$EXPORT" ".csv"
ROW_COUNT=$(echo "$EXPORT" | jq -r '.row_count' 2>/dev/null)
if [ "$ROW_COUNT" -ge 0 ] 2>/dev/null; then
    pass "Export row_count=$ROW_COUNT"
else
    fail "Export sales" "missing row_count"
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 8: SSH Keys
# ══════════════════════════════════════════════════════════════════
echo "── 8. SSH Keys ──"

SSH_KEYS=$(api GET "/api/internal/creator/ssh-keys?user_id=$ELENA_ID")
SSH_COUNT=$(echo "$SSH_KEYS" | jq 'length' 2>/dev/null)
if [ "$SSH_COUNT" -ge 0 ] 2>/dev/null; then
    pass "List SSH keys (count=$SSH_COUNT)"
else
    fail "List SSH keys" "did not return array"
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 9: SSH Key Lookup
# ══════════════════════════════════════════════════════════════════
echo "── 9. SSH Key Lookup ──"

# Test with a fake fingerprint (should 404)
LOOKUP_STATUS=$(api_status GET "/api/internal/ssh-key-lookup?fingerprint=SHA256:fakefingerprint123")
if [ "$LOOKUP_STATUS" = "404" ]; then
    pass "SSH key lookup 404 for unknown key"
else
    fail "SSH key lookup unknown" "expected 404, got $LOOKUP_STATUS"
fi

# Insert a test SSH key and look it up
TEST_FP="SHA256:test$(date +%s)"
DB_NAME="${MNW_DB:-makenotwork_staging}"
psql "$DB_NAME" -c "INSERT INTO ssh_keys (user_id, public_key, fingerprint, label) VALUES ('$ELENA_ID', 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAItest', '$TEST_FP', 'test-key') ON CONFLICT DO NOTHING;" 2>/dev/null

LOOKUP=$(api GET "/api/internal/ssh-key-lookup?fingerprint=$TEST_FP" 2>/dev/null || echo '{}')
LOOKUP_USER=$(echo "$LOOKUP" | jq -r '.username' 2>/dev/null)
if [ "$LOOKUP_USER" = "elena" ]; then
    pass "SSH key lookup returns correct user"
else
    fail "SSH key lookup" "expected elena, got $LOOKUP_USER"
fi

# Clean up test key
psql "$DB_NAME" -c "DELETE FROM ssh_keys WHERE fingerprint = '$TEST_FP';" 2>/dev/null

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 10: Upload Pipeline (presign + confirm)
# ══════════════════════════════════════════════════════════════════
echo "── 10. Upload Pipeline ──"

if [ -z "$NEW_ITEM_ID" ]; then
    skip "Upload presign" "no test item created"
    skip "Upload confirm" "no test item created"
elif ! $HAS_FFMPEG; then
    skip "Upload presign" "no ffmpeg"
    skip "Upload confirm" "no ffmpeg"
else
    # Presign for audio upload
    PRESIGN=$(api POST "/api/internal/upload/presign" \
        -d "{\"user_id\":\"$ELENA_ID\",\"item_id\":\"$NEW_ITEM_ID\",\"file_type\":\"audio\",\"file_name\":\"test_track.wav\",\"content_type\":\"audio/wav\"}" 2>/dev/null || echo '{}')
    UPLOAD_URL=$(echo "$PRESIGN" | jq -r '.upload_url' 2>/dev/null)
    S3_KEY=$(echo "$PRESIGN" | jq -r '.s3_key' 2>/dev/null)

    if [ -n "$UPLOAD_URL" ] && [ "$UPLOAD_URL" != "null" ]; then
        pass "Presign upload (s3_key=$S3_KEY)"
        assert_json_field "Presign has expires_in" "$PRESIGN" ".expires_in"

        # Upload to S3
        S3_STATUS=$(curl -s -o /dev/null -w '%{http_code}' -X PUT \
            -H "Content-Type: audio/wav" \
            --data-binary "@$TMPDIR/test_track.wav" \
            "$UPLOAD_URL")
        if [ "$S3_STATUS" = "200" ]; then
            pass "S3 upload (HTTP $S3_STATUS)"

            # Confirm upload
            CONFIRM=$(api POST "/api/internal/upload/confirm" \
                -d "{\"user_id\":\"$ELENA_ID\",\"item_id\":\"$NEW_ITEM_ID\",\"file_type\":\"audio\",\"s3_key\":\"$S3_KEY\"}" 2>/dev/null || echo '{}')
            CONFIRM_OK=$(echo "$CONFIRM" | jq -r '.success' 2>/dev/null)
            if [ "$CONFIRM_OK" = "true" ]; then
                pass "Confirm upload"
            else
                fail "Confirm upload" "success=$CONFIRM_OK (response: $CONFIRM)"
            fi
        else
            fail "S3 upload" "HTTP $S3_STATUS"
            skip "Confirm upload" "S3 upload failed"
        fi
    else
        # Presign may fail if no S3 configured on staging — that's expected
        skip "Presign upload" "no S3 configured (response: $(echo "$PRESIGN" | head -c 200))"
        skip "S3 upload" "no presign URL"
        skip "Confirm upload" "no presign URL"
    fi
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 11: Error Cases
# ══════════════════════════════════════════════════════════════════
echo "── 11. Error Cases ──"

# Bad auth
BAD_AUTH=$(curl -s -o /dev/null -w '%{http_code}' -X GET \
    -H "Authorization: Bearer wrong-token" \
    "${MNW_URL}/api/internal/creator/projects?user_id=$ELENA_ID")
if [ "$BAD_AUTH" = "401" ]; then
    pass "Bad auth returns 401"
else
    fail "Bad auth" "expected 401, got $BAD_AUTH"
fi

# No auth header
NO_AUTH=$(curl -s -o /dev/null -w '%{http_code}' -X GET \
    "${MNW_URL}/api/internal/creator/projects?user_id=$ELENA_ID")
if [ "$NO_AUTH" = "401" ]; then
    pass "No auth returns 401"
else
    fail "No auth" "expected 401, got $NO_AUTH"
fi

# Nonexistent user
FAKE_USER="99999999-9999-9999-9999-999999999999"
assert_status "Nonexistent user projects" 200 GET "/api/internal/creator/projects?user_id=$FAKE_USER"
FAKE_PROJECTS=$(api GET "/api/internal/creator/projects?user_id=$FAKE_USER")
assert_json_count "Nonexistent user empty projects" "$FAKE_PROJECTS" 0

# Nonexistent item detail
FAKE_ITEM="99999999-9999-9999-9999-999999999999"
FAKE_STATUS=$(api_status GET "/api/internal/creator/items/$FAKE_ITEM?user_id=$ELENA_ID")
if [ "$FAKE_STATUS" = "404" ] || [ "$FAKE_STATUS" = "403" ]; then
    pass "Nonexistent item returns $FAKE_STATUS"
else
    fail "Nonexistent item" "expected 404/403, got $FAKE_STATUS"
fi

# Wrong owner for item
FAKE_OWNER="99999999-9999-9999-9999-999999999998"
WRONG_STATUS=$(api_status GET "/api/internal/creator/items/$ITEM_ESSAY?user_id=$FAKE_OWNER")
if [ "$WRONG_STATUS" = "404" ] || [ "$WRONG_STATUS" = "403" ]; then
    pass "Wrong owner returns $WRONG_STATUS"
else
    fail "Wrong owner" "expected 404/403, got $WRONG_STATUS"
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Section 12: Cleanup
# ══════════════════════════════════════════════════════════════════
echo "── 12. Cleanup ──"

# Delete the test item we created
if [ -n "$NEW_ITEM_ID" ]; then
    assert_status "Delete test item" 204 DELETE "/api/internal/creator/items/$NEW_ITEM_ID?user_id=$ELENA_ID"
fi

echo ""

# ══════════════════════════════════════════════════════════════════
# Summary
# ══════════════════════════════════════════════════════════════════
TOTAL=$((PASS + FAIL + SKIP))
echo "════════════════════════════════════════"
echo "  Results: $PASS passed, $FAIL failed, $SKIP skipped ($TOTAL total)"
echo "════════════════════════════════════════"

if [ $FAIL -gt 0 ]; then
    echo ""
    echo "Failures:$ERRORS"
    echo ""
    exit 1
fi

exit 0
