#!/bin/bash
# xcode-validate.sh — Validates iOS/watchOS project structure and configuration
# Usage: ./scripts/xcode-validate.sh [project-root]
#
# Checks:
#   1. Required directory structure exists
#   2. Info.plist files present for each target
#   3. Entitlements files referenced correctly
#   4. Bundle ID conventions followed
#   5. Minimum deployment targets set
#   6. Required capabilities declared
#   7. Watch app bundle ID matches iOS parent
#   8. Code signing configuration present

set -euo pipefail

ROOT="${1:-.}"
ERRORS=0
WARNINGS=0

red()    { printf "\033[31m%s\033[0m\n" "$1"; }
yellow() { printf "\033[33m%s\033[0m\n" "$1"; }
green()  { printf "\033[32m%s\033[0m\n" "$1"; }
dim()    { printf "\033[2m%s\033[0m\n" "$1"; }

error()   { red   "  ERROR: $1"; ((ERRORS++)); }
warn()    { yellow "  WARN:  $1"; ((WARNINGS++)); }
pass()    { green  "  PASS:  $1"; }
section() { echo ""; echo "=== $1 ==="; }

# --- 1. Project file detection ---
section "Project Configuration"

if [ -f "$ROOT/project.yml" ]; then
    pass "XcodeGen project.yml found"
    PROJECT_TYPE="xcodegen"
elif ls "$ROOT"/*.xcodeproj &>/dev/null; then
    pass "Xcode project found"
    PROJECT_TYPE="xcodeproj"
elif [ -f "$ROOT/Package.swift" ]; then
    pass "Swift Package found"
    PROJECT_TYPE="spm"
else
    error "No project.yml, .xcodeproj, or Package.swift found"
    PROJECT_TYPE="unknown"
fi

# --- 2. Directory structure ---
section "Directory Structure"

for dir in Shared iOS watchOS; do
    if [ -d "$ROOT/$dir" ]; then
        pass "$dir/ directory exists"
    else
        if [ "$dir" = "Shared" ]; then
            warn "$dir/ directory missing (recommended for multiplatform)"
        else
            dim "  SKIP: $dir/ not present (single-platform project?)"
        fi
    fi
done

# Check for recommended subdirectories
for subdir in Models Services Protocols Extensions; do
    if [ -d "$ROOT/Shared/$subdir" ]; then
        pass "Shared/$subdir/ exists"
    elif [ -d "$ROOT/Shared" ]; then
        warn "Shared/$subdir/ missing (recommended)"
    fi
done

# --- 3. Info.plist files ---
section "Info.plist Files"

find "$ROOT" -name "Info.plist" -not -path "*/build/*" -not -path "*/.build/*" -not -path "*/DerivedData/*" | while read -r plist; do
    relative="${plist#$ROOT/}"
    pass "Found: $relative"
done

PLIST_COUNT=$(find "$ROOT" -name "Info.plist" -not -path "*/build/*" -not -path "*/.build/*" -not -path "*/DerivedData/*" 2>/dev/null | wc -l | tr -d ' ')
if [ "$PLIST_COUNT" -eq 0 ]; then
    warn "No Info.plist files found (may be generated by build system)"
fi

# --- 4. Entitlements ---
section "Entitlements"

find "$ROOT" -name "*.entitlements" -not -path "*/build/*" | while read -r ent; do
    relative="${ent#$ROOT/}"
    pass "Found: $relative"

    # Check for common capabilities
    if grep -q "HealthKit" "$ent" 2>/dev/null; then
        pass "  HealthKit entitlement declared"
    fi
    if grep -q "com.apple.developer.networking.wifi-info" "$ent" 2>/dev/null; then
        pass "  WiFi info entitlement declared"
    fi
done

# --- 5. Bundle ID conventions ---
section "Bundle Identifiers"

if [ "$PROJECT_TYPE" = "xcodegen" ] && [ -f "$ROOT/project.yml" ]; then
    # Extract bundle IDs from project.yml
    grep -E "PRODUCT_BUNDLE_IDENTIFIER|bundleIdPrefix" "$ROOT/project.yml" 2>/dev/null | while read -r line; do
        dim "  Found: $line"
    done

    # Check watchOS bundle ID convention
    if grep -q "watchkitapp" "$ROOT/project.yml" 2>/dev/null; then
        pass "watchOS bundle ID follows .watchkitapp convention"
    elif [ -d "$ROOT/watchOS" ]; then
        warn "watchOS target exists but no .watchkitapp bundle ID found"
    fi
fi

# --- 6. Swift version ---
section "Swift Configuration"

if [ -f "$ROOT/.swift-version" ]; then
    SWIFT_VER=$(cat "$ROOT/.swift-version")
    pass "Swift version pinned: $SWIFT_VER"
elif [ "$PROJECT_TYPE" = "xcodegen" ]; then
    if grep -q "SWIFT_VERSION" "$ROOT/project.yml" 2>/dev/null; then
        SWIFT_VER=$(grep "SWIFT_VERSION" "$ROOT/project.yml" | head -1 | awk '{print $NF}' | tr -d '"')
        pass "Swift version in project.yml: $SWIFT_VER"
    else
        warn "No SWIFT_VERSION specified in project.yml"
    fi
fi

# --- 7. Deployment targets ---
section "Deployment Targets"

if [ "$PROJECT_TYPE" = "xcodegen" ] && [ -f "$ROOT/project.yml" ]; then
    # Check iOS target
    IOS_TARGET=$(grep -A2 "iOS\|IPHONEOS_DEPLOYMENT_TARGET" "$ROOT/project.yml" | grep -oE "[0-9]+\.[0-9]+" | head -1)
    if [ -n "$IOS_TARGET" ]; then
        IOS_MAJOR=$(echo "$IOS_TARGET" | cut -d. -f1)
        if [ "$IOS_MAJOR" -ge 17 ]; then
            pass "iOS deployment target: $IOS_TARGET (supports @Observable, SwiftData)"
        else
            warn "iOS deployment target: $IOS_TARGET (< 17.0, no @Observable/SwiftData)"
        fi
    fi

    # Check watchOS target
    WATCH_TARGET=$(grep -A2 "watchOS\|WATCHOS_DEPLOYMENT_TARGET" "$ROOT/project.yml" | grep -oE "[0-9]+\.[0-9]+" | head -1)
    if [ -n "$WATCH_TARGET" ]; then
        WATCH_MAJOR=$(echo "$WATCH_TARGET" | cut -d. -f1)
        if [ "$WATCH_MAJOR" -ge 10 ]; then
            pass "watchOS deployment target: $WATCH_TARGET (supports @Observable, SwiftData)"
        else
            warn "watchOS deployment target: $WATCH_TARGET (< 10.0, no @Observable/SwiftData)"
        fi
    fi
fi

# --- 8. Watch connectivity ---
section "Watch Connectivity"

if [ -d "$ROOT/watchOS" ] || [ -d "$ROOT/Watch" ]; then
    # Check for WCSession usage
    WC_FILES=$(grep -rl "WCSession\|WatchConnectivity" "$ROOT" --include="*.swift" 2>/dev/null | grep -v build | grep -v DerivedData | wc -l | tr -d ' ')
    if [ "$WC_FILES" -gt 0 ]; then
        pass "WatchConnectivity used in $WC_FILES files"
    else
        warn "watchOS target exists but no WatchConnectivity usage found"
    fi

    # Check for companion app bundle ID
    if grep -rl "WKCompanionAppBundleIdentifier" "$ROOT" --include="*.plist" --include="*.yml" 2>/dev/null | head -1 > /dev/null; then
        pass "Companion app bundle ID configured"
    else
        warn "No WKCompanionAppBundleIdentifier found"
    fi
fi

# --- 9. Common anti-patterns ---
section "Anti-Pattern Check"

# Check for nested TabView on watchOS
NESTED_TAB=$(grep -rn "TabView" "$ROOT/watchOS" --include="*.swift" 2>/dev/null | wc -l | tr -d ' ')
if [ "$NESTED_TAB" -gt 1 ]; then
    warn "Multiple TabView instances in watchOS code (nested TabView causes memory leaks)"
fi

# Check for #Unique with CloudKit
if grep -r "#Unique" "$ROOT" --include="*.swift" 2>/dev/null | grep -v build | grep -v DerivedData | head -1 > /dev/null 2>&1; then
    if grep -r "cloudKit\|CloudKit\|CKContainer" "$ROOT" --include="*.swift" 2>/dev/null | grep -v build | head -1 > /dev/null 2>&1; then
        error "#Unique constraint found with CloudKit usage (unsupported, causes silent failures)"
    fi
fi

# Check for @State with ObservableObject (not @Observable)
STATE_OBJ=$(grep -rn "@State.*=.*\(\)" "$ROOT" --include="*.swift" 2>/dev/null | grep -v build | grep -v DerivedData | grep -v "@Observable" | wc -l | tr -d ' ')
if [ "$STATE_OBJ" -gt 5 ]; then
    warn "Many @State initializations found ($STATE_OBJ) -- verify none use reference types without @Observable"
fi

# --- Summary ---
section "Summary"

if [ "$ERRORS" -gt 0 ]; then
    red "  $ERRORS error(s), $WARNINGS warning(s)"
    exit 1
elif [ "$WARNINGS" -gt 0 ]; then
    yellow "  0 errors, $WARNINGS warning(s)"
    exit 0
else
    green "  All checks passed!"
    exit 0
fi
