#!/usr/bin/env bash
#
# Daily Notification Plugin - Verification Script
#
# Single entrypoint to validate the project state.
# Used by CI and local development.
#
# @author Matthew Raymer
# @version 1.0.0

set -euo pipefail

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

# Counters
PASSED=0
FAILED=0
SKIPPED=0

# Logging functions
print_header() {
    echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "${BLUE}$1${NC}"
    echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
}

print_info() {
    echo -e "${BLUE}ℹ${NC} $1"
}

print_success() {
    echo -e "${GREEN}✓${NC} $1"
    ((PASSED++))
}

print_error() {
    echo -e "${RED}✗${NC} $1"
    ((FAILED++))
}

print_warning() {
    echo -e "${YELLOW}⚠${NC} $1"
    ((SKIPPED++))
}

# Check if command exists
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# Run command and capture result
run_check() {
    local name="$1"
    shift
    print_info "Checking: $name"
    
    # Capture output for debugging on failure
    local output
    output=$("$@" 2>&1)
    local exit_code=$?
    
    if [ $exit_code -eq 0 ]; then
        print_success "$name"
        return 0
    else
        print_error "$name"
        # Print captured output on failure for debugging
        echo ""
        echo "Command output:"
        echo "$output" | head -20
        if [ $(echo "$output" | wc -l) -gt 20 ]; then
            echo "... (truncated, showing first 20 lines)"
        fi
        echo ""
        return 1
    fi
}

# Print environment diagnostics
print_environment() {
    print_header "Environment Diagnostics"
    
    echo "Project Root: $PROJECT_ROOT"
    echo "Script Directory: $SCRIPT_DIR"
    echo ""
    
    # Node.js
    if command_exists node; then
        echo "Node.js: $(node --version)"
    else
        echo "Node.js: ❌ Not found"
    fi
    
    # npm
    if command_exists npm; then
        echo "npm: $(npm --version)"
    else
        echo "npm: ❌ Not found"
    fi
    
    # Java (for Android)
    if command_exists java; then
        echo "Java: $(java -version 2>&1 | head -n 1)"
    else
        echo "Java: ⚠ Not found (Android builds may fail)"
    fi
    
    # Gradle (for Android)
    if command_exists gradle; then
        echo "Gradle: $(gradle --version 2>&1 | grep 'Gradle' | head -n 1 || echo 'Unknown')"
    else
        echo "Gradle: ⚠ Not found (using wrapper)"
    fi
    
    # Swift (for iOS)
    if command_exists swift; then
        echo "Swift: $(swift --version 2>&1 | head -n 1)"
    else
        echo "Swift: ⚠ Not found (iOS builds may fail)"
    fi
    
    # xcodebuild (for iOS)
    if command_exists xcodebuild; then
        echo "xcodebuild: $(xcodebuild -version 2>&1 | head -n 1)"
    else
        echo "xcodebuild: ⚠ Not found (iOS builds may fail)"
    fi
    
    echo ""
}

# Install dependencies (best effort)
install_dependencies() {
    print_header "Installing Dependencies"
    
    if [ ! -d "$PROJECT_ROOT/node_modules" ]; then
        print_info "Installing npm dependencies..."
        cd "$PROJECT_ROOT"
        npm install || print_warning "npm install failed (non-blocking)"
    else
        print_success "Dependencies already installed"
    fi
    echo ""
}

# TypeScript checks
check_typescript() {
    print_header "TypeScript Checks"
    
    cd "$PROJECT_ROOT"
    
    # Type check
    if run_check "TypeScript compilation" npm run typecheck; then
        :
    else
        print_error "TypeScript type checking failed"
        return 1
    fi
    
    # Lint
    if run_check "ESLint" npm run lint; then
        :
    else
        print_warning "ESLint found issues (non-blocking)"
    fi
    
    # Unit tests (if present)
    if [ -f "$PROJECT_ROOT/package.json" ] && grep -q '"test"' "$PROJECT_ROOT/package.json"; then
        if run_check "Unit tests" npm test; then
            :
        else
            print_warning "Unit tests failed (non-blocking)"
        fi
    else
        print_warning "No unit tests configured"
    fi
    
    echo ""
}

# Build checks
check_build() {
    print_header "Build Checks"
    
    cd "$PROJECT_ROOT"
    
    # Run build
    if run_check "npm run build" npm run build; then
        :
    else
        print_error "Build failed - this will break publish"
        return 1
    fi
    
    # Verify dist/ exists
    if [ ! -d "$PROJECT_ROOT/dist" ]; then
        print_error "dist/ directory not found after build"
        return 1
    else
        print_success "dist/ directory exists"
    fi
    
    echo ""
}

# Package checks
check_package() {
    print_header "Package Checks"
    
    cd "$PROJECT_ROOT"
    
    # Run npm pack --dry-run
    print_info "Running npm pack --dry-run..."
    PACK_OUTPUT=$(npm pack --dry-run 2>&1)
    PACK_EXIT=$?
    
    if [ $PACK_EXIT -ne 0 ]; then
        print_error "npm pack --dry-run failed"
        echo "$PACK_OUTPUT"
        return 1
    fi
    
    # Extract file list from pack output (handle both "===" and plain "Tarball Contents" formats)
    # Only include actual file entries (lines starting with size like "556B", "1.1kB", etc.)
    # This excludes metadata lines like "filename: ... .tgz" from Tarball Details section
    PACK_FILES=$(echo "$PACK_OUTPUT" | grep -A 10000 -E "npm notice === Tarball Contents ===|npm notice Tarball Contents" | grep "npm notice" | sed 's/npm notice //' | grep -v "^===" | grep -v "^Tarball Contents$" | grep -E '^[0-9]')
    
    # If still empty, try alternative format (without "===" header)
    if [ -z "$PACK_FILES" ]; then
        # Extract only file entries (lines starting with size pattern)
        PACK_FILES=$(echo "$PACK_OUTPUT" | grep "npm notice" | sed 's/npm notice //' | grep -E '^[0-9]' | grep -v "^package size:" | grep -v "^$")
    fi
    
    # If still empty, fallback to all npm notice lines (but exclude known metadata)
    if [ -z "$PACK_FILES" ]; then
        PACK_FILES=$(echo "$PACK_OUTPUT" | grep "npm notice" | sed 's/npm notice //' | grep -v "^package size:" | grep -v "^name:" | grep -v "^version:" | grep -v "^filename:" | grep -v "^shasum:" | grep -v "^integrity:" | grep -v "^total files:" | grep -v "^$")
    fi
    
    # Check for required files
    if echo "$PACK_FILES" | grep -q "CapacitorDailyNotification.podspec"; then
        print_success "Podspec included in package"
    else
        print_error "Podspec missing from package (check package.json 'files' field)"
        return 1
    fi
    
    if echo "$PACK_FILES" | grep -q "dist/"; then
        print_success "dist/ included in package"
    else
        print_error "dist/ missing from package"
        return 1
    fi
    
    if echo "$PACK_FILES" | grep -q "android/"; then
        print_success "android/ included in package"
    else
        print_warning "android/ not in package (may be intentional)"
    fi
    
    if echo "$PACK_FILES" | grep -q "ios/"; then
        print_success "ios/ included in package"
    else
        print_warning "ios/ not in package (may be intentional)"
    fi
    
    # Check for forbidden files (hard fail)
    # Patterns: Xcode user state, build artifacts, test apps, editor temp files, macOS junk
    FORBIDDEN_PATTERNS="xcuserdata/|\.xcuserstate|DerivedData/|\.tgz|ios/App/|\.DS_Store|\.swp|\.swo|\.orig|\.rej"
    FORBIDDEN_FOUND=$(echo "$PACK_FILES" | grep -E "$FORBIDDEN_PATTERNS" || true)
    
    if [ -n "$FORBIDDEN_FOUND" ]; then
        print_error "Forbidden files found in package (update package.json 'files' field):"
        echo "$FORBIDDEN_FOUND" | while read -r line; do
            echo "  - $line"
        done
        print_info "Fix: Tighten package.json 'files' field to exclude ios/App/ and Xcode user state files"
        print_info "Or add to .npmignore: **/xcuserdata/**, **/*.xcuserstate, **/DerivedData/**, ios/App/**, .DS_Store, *.swp, *.swo, *.orig, *.rej"
        return 1
    else
        print_success "No forbidden files (xcuserdata, xcuserstate, DerivedData, ios/App/, .DS_Store, editor temp files) in package"
    fi
    
    # Check for unwanted files (warnings)
    if echo "$PACK_FILES" | grep -q "test-apps/"; then
        print_warning "test-apps/ found in package (should be excluded)"
    fi
    
    if echo "$PACK_FILES" | grep -q "doc/"; then
        print_warning "doc/ found in package (should be excluded)"
    fi
    
    if echo "$PACK_FILES" | grep -q "node_modules/"; then
        print_warning "node_modules/ found in package (should be excluded)"
    fi
    
    # Print package manifest summary (first 20 lines)
    print_info "Package manifest summary (showing first 20 of $(echo "$PACK_FILES" | wc -l) files):"
    echo "$PACK_FILES" | head -20 | while read -r line; do
        echo "  $line"
    done
    TOTAL_FILES=$(echo "$PACK_FILES" | wc -l)
    if [ "$TOTAL_FILES" -gt 20 ]; then
        print_info "... and $((TOTAL_FILES - 20)) more files"
    fi
    
    echo ""
}

# Android checks (best effort)
check_android() {
    print_header "Android Checks"
    
    cd "$PROJECT_ROOT"
    
    if [ ! -d "$PROJECT_ROOT/android" ]; then
        print_warning "Android directory not found, skipping Android checks"
        return 0
    fi
    
    if ! command_exists java; then
        print_warning "Java not found, skipping Android build checks"
        return 0
    fi
    
    cd "$PROJECT_ROOT/android"
    
    # Check if gradlew exists
    if [ ! -f "$PROJECT_ROOT/android/gradlew" ]; then
        print_warning "gradlew not found, skipping Android build"
        return 0
    fi
    
    # Try to run a minimal gradle task
    # Note: This may fail if Capacitor Android is not available as a dependency
    # (it's only available in a Capacitor app context, not standalone)
    # So we catch the error and treat it as non-blocking
    set +e
    local gradle_output
    gradle_output=$(./gradlew compileDebugJavaWithJavac --no-daemon 2>&1)
    local gradle_exit=$?
    set -e
    
    if [ $gradle_exit -eq 0 ]; then
        print_success "Android build (compile)"
    else
        print_warning "Android build check failed (non-blocking - expected in standalone context)"
        print_info "Android plugin requires Capacitor app context to build"
    fi
    
    echo ""
}

# iOS checks (best effort)
check_ios() {
    print_header "iOS Checks"
    
    cd "$PROJECT_ROOT"
    
    if [ ! -d "$PROJECT_ROOT/ios" ]; then
        print_warning "iOS directory not found, skipping iOS checks"
        return 0
    fi
    
    if ! command_exists xcodebuild; then
        print_warning "xcodebuild not found, skipping iOS build checks"
        print_info "Manual iOS build command: cd ios && xcodebuild -workspace DailyNotificationPlugin.xcworkspace -scheme DailyNotificationPlugin -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' build"
        return 0
    fi
    
    # Check if Podfile exists
    if [ ! -f "$PROJECT_ROOT/ios/Podfile" ]; then
        print_warning "Podfile not found, skipping iOS build"
        return 0
    fi
    
    # Try to build (best effort, may fail in CI)
    # Note: Don't use pipe in run_check - it won't work. Capture output separately.
    cd "$PROJECT_ROOT/ios"
    BUILD_OUTPUT=$(xcodebuild -workspace DailyNotificationPlugin.xcworkspace -scheme DailyNotificationPlugin -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' build 2>&1)
    BUILD_EXIT=$?
    
    if [ $BUILD_EXIT -eq 0 ]; then
        print_success "iOS build (compile)"
        # Show first 10 lines of output for context
        echo "$BUILD_OUTPUT" | head -10 | while read -r line; do
            echo "  $line"
        done
    else
        print_warning "iOS build check failed (non-blocking - may require manual setup)"
        print_info "Manual iOS build command: cd ios && xcodebuild -workspace DailyNotificationPlugin.xcworkspace -scheme DailyNotificationPlugin -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' build"
    fi
    
    # Try to run tests (best effort)
    print_info "Running iOS tests..."
    TEST_OUTPUT=$(xcodebuild test -workspace DailyNotificationPlugin.xcworkspace -scheme DailyNotificationPlugin -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' -only-testing:DailyNotificationPluginTests/DailyNotificationRecoveryTests 2>&1)
    TEST_EXIT=$?
    
    if [ $TEST_EXIT -eq 0 ]; then
        print_success "iOS recovery tests passed"
        # Show test summary if available
        echo "$TEST_OUTPUT" | grep -E "Test Suite|Test Case|passed|failed" | head -10 | while read -r line; do
            echo "  $line"
        done
    else
        print_warning "iOS tests failed or not available (non-blocking)"
        print_info "Manual iOS test command: cd ios && xcodebuild test -workspace DailyNotificationPlugin.xcworkspace -scheme DailyNotificationPlugin -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15'"
    fi
    
    echo ""
}

# Check for native code in src/
# Check core module source (can run before build)
check_core_source() {
    print_header "Core Module (Source) Checks"
    cd "$PROJECT_ROOT"
    
    # Require core source dir + expected files
    if [ ! -d "src/core" ]; then
        print_error "Missing src/core/"
        return 1
    fi
    
    local required=(
        "src/core/index.ts"
        "src/core/errors.ts"
        "src/core/enums.ts"
        "src/core/events.ts"
        "src/core/contracts.ts"
        "src/core/guards.ts"
    )
    
    local missing=0
    for f in "${required[@]}"; do
        if [ ! -f "$f" ]; then
            print_error "Missing core file: $f"
            missing=1
        fi
    done
    if [ $missing -ne 0 ]; then
        return 1
    fi
    
    # No platform imports inside core
    # Block Node builtins, React, Capacitor, and other platform-specific modules
    local NODE_BUILTINS="(fs|path|os|child_process|crypto|http|https|net|tls|zlib|stream|util|url|worker_threads|perf_hooks|vm)"
    local bad
    bad=$(grep -RInE \
        "(from\s+['\"]|require\s*\(\s*['\"]|import\s*\(\s*['\"])(${NODE_BUILTINS}|react|@capacitor/|capacitor)['\"]" \
        src/core 2>/dev/null || true)
    
    if [ -n "$bad" ]; then
        print_error "Core module contains forbidden platform imports:"
        echo "$bad" | head -50 | while read -r line; do
            echo "  $line"
        done
        echo ""
        echo "Policy: src/core must not import platform, Node, or framework-specific modules."
        echo "Move platform-dependent code to src/web/ or platform adapters."
        return 1
    fi
    
    print_success "Core source checks passed"
    echo ""
}

# Check core module build artifacts (must run after build)
check_core_artifacts() {
    print_header "Core Module (Build Artifacts) Checks"
    cd "$PROJECT_ROOT"
    
    # Require build outputs for core
    local required=(
        "dist/esm/core/index.js"
        "dist/esm/core/index.d.ts"
    )
    
    local missing=0
    for f in "${required[@]}"; do
        if [ ! -f "$f" ]; then
            print_error "Missing build artifact: $f (did build run?)"
            missing=1
        fi
    done
    if [ $missing -ne 0 ]; then
        return 1
    fi
    
    # Require package.json export for ./core
    if ! node -e "const p=require('./package.json'); if(!p.exports||!p.exports['./core']) process.exit(1);" 2>/dev/null; then
        print_error "package.json missing exports['./core']"
        return 1
    fi
    
    print_success "Core artifact checks passed"
    echo ""
}

check_native_code_in_src() {
    print_header "Checking for Native Code in src/"
    
    cd "$PROJECT_ROOT"
    
    # Check for Java files
    if find src/android -name "*.java" -type f 2>/dev/null | grep -q .; then
        print_error "Found Java files in src/android/ (should be removed)"
        find src/android -name "*.java" -type f 2>/dev/null | while read -r file; do
            echo "  - $file"
        done
        return 1
    else
        print_success "No Java files in src/android/"
    fi
    
    # Check for Swift/Objective-C files
    if find src/ios -name "*.swift" -o -name "*.m" -o -name "*.mm" -o -name "*.h" 2>/dev/null | grep -q .; then
        print_error "Found native code files in src/ios/ (should be removed)"
        find src/ios -name "*.swift" -o -name "*.m" -o -name "*.mm" -o -name "*.h" 2>/dev/null | while read -r file; do
            echo "  - $file"
        done
        return 1
    else
        print_success "No native code files in src/ios/"
    fi
    
    echo ""
}

# Version consistency check
check_version_consistency() {
    print_header "Version Consistency Check"
    cd "$PROJECT_ROOT"
    
    if [ -f "scripts/check-version-consistency.sh" ]; then
        if bash scripts/check-version-consistency.sh; then
            print_success "Version consistency check passed"
            echo ""
            return 0
        else
            print_error "Version consistency check failed"
            echo ""
            return 1
        fi
    else
        print_warning "Version check script not found, skipping"
        echo ""
        return 0
    fi
}

# Main execution
main() {
    print_header "Daily Notification Plugin - Verification"
    
    print_environment
    install_dependencies
    
    # Run checks - use || true to prevent set -e from exiting on failure
    # We track failures in FAILED counter and exit at the end if any critical checks failed
    run_check "Version consistency" check_version_consistency || true
    run_check "Native code not in src/" check_native_code_in_src || true
    
    # Core source checks must be before build
    run_check "Core module source checks" check_core_source || true
    
    run_check "TypeScript typecheck" check_typescript || true
    run_check "Build" check_build || true
    
    # Core artifacts checks must be after build
    run_check "Core module artifact checks" check_core_artifacts || true
    
    run_check "Package checks" check_package || true
    
    check_android
    check_ios
    
    # Summary
    print_header "Verification Summary"
    echo "Passed: $PASSED"
    echo "Failed: $FAILED"
    echo "Skipped: $SKIPPED"
    echo ""
    
    if [ $FAILED -eq 0 ]; then
        print_success "All critical checks passed!"
        exit 0
    else
        print_error "Some checks failed. Review output above."
        exit 1
    fi
}

# Run main
main "$@"

