#!/bin/bash
# Build and run Kordant application
# Usage: ./run [build|test|run|lsp] [-v|--verbose] [-c|--coverage] [-p|--performance] [-o|--output <file>] [--no-lsp]
# Note: Default action (./run with no args) runs the app with verbose logging enabled

set -o pipefail

readonly PROJECT="Kordant.xcodeproj"
readonly SCHEME="Kordant"
readonly CONFIGURATION="Debug"
readonly APP_SUBSYSTEM="com.frenocorp.Kordant"
readonly BUNDLE_ID="com.frenocorp.lendair"

VERBOSE=false
OUTPUT_FILE=""
SKIP_LSP=false
PERFORMANCE=false
COVERAGE=false

build_xcodebuild_command() {
    local action="$1"
    local destination="${2:-generic/platform=iOS}"
    local extra_flags="${3:-}"

    local cmd="xcodebuild -project $PROJECT -scheme $SCHEME -configuration $CONFIGURATION -destination '$destination' $extra_flags $action"

    if [ "$PERFORMANCE" = true ]; then
        cmd="$cmd -enablePerformanceTestsDiagnostics YES"
    fi

    if [ "$COVERAGE" = true ]; then
        cmd="$cmd -enableCodeCoverage YES"
    fi

    echo "$cmd"
}

kill_existing_lendair_processes() {
    echo "Checking for existing Kordant processes..."

    local pids
    pids=$(pgrep -f "Kordant.app")
    if [ -n "$pids" ]; then
        echo "Killing existing Kordant processes (PID(s): $pids)..."
        kill $pids 2>/dev/null
        sleep 1
    else
        echo "No existing Kordant processes found"
    fi
}

update_lsp_config() {
    echo "Updating LSP configuration..."

    if command -v xcode-build-server &> /dev/null; then
        local build_root
        build_root=$(ls -td "$HOME/Library/Developer/Xcode/DerivedData/${SCHEME}-"*/Build 2>/dev/null | head -1)

        local exit_code
        if [ -n "$build_root" ]; then
            xcode-build-server config -project "$PROJECT" -scheme "$SCHEME" --build_root "$build_root" > /dev/null 2>&1
            exit_code=$?
        else
            xcode-build-server config -project "$PROJECT" -scheme "$SCHEME" > /dev/null 2>&1
            exit_code=$?
        fi

        if [ $exit_code -eq 0 ]; then
            echo "LSP configuration updated (buildServer.json created)"
        else
            echo "Could not update LSP configuration"
        fi
    else
        echo "xcode-build-server not found. Install with: brew install xcode-build-server"
        echo "  This helps Neovim's LSP recognize your Swift modules"
    fi
}

get_build_directory() {
    xcodebuild -project "$PROJECT" -scheme "$SCHEME" -configuration "$CONFIGURATION" \
        -showBuildSettings 2>/dev/null | \
        grep -m 1 "BUILT_PRODUCTS_DIR" | \
        sed 's/.*= //'
}

handle_build_success() {
    echo "Build succeeded!"

    if [ "$SKIP_LSP" != true ]; then
        update_lsp_config
    fi
}

print_errors() {
    local output="$1"
    local action_type="$2"

    echo ""
    echo "${action_type} Errors:"
    echo "================================================================================"

    local errors
    errors=$(echo "$output" | grep -E "error:|Error |failed|FAIL" | sed 's/^/  /')

    if [ -n "$errors" ]; then
        echo "$errors"
    else
        echo "  No specific error messages found. See full output above."
    fi

    echo "================================================================================"
}

print_warnings() {
    local output="$1"

    echo ""
    echo "Diagnostic Warnings:"
    echo "================================================================================"

    local warnings
    warnings=$(echo "$output" | grep -E "\.swift:[0-9]+:[0-9]+: warning:" | sed 's/^/  /')

    if [ -n "$warnings" ]; then
        local count
        count=$(echo "$warnings" | wc -l | tr -d ' ')
        echo "  Found $count warning(s):"
        echo ""
        echo "$warnings"
    else
        echo "  No warnings found."
    fi

    echo "================================================================================"
}

get_booted_simulator() {
    xcrun simctl list devices booted 2>/dev/null | grep -oE "[A-F0-9-]{36}" | head -1
}

ensure_simulator_booted() {
    local simulator
    simulator=$(get_booted_simulator)
    if [ -z "$simulator" ]; then
        echo "No booted simulator found, booting first available iPhone..." >&2
        simulator=$(xcrun simctl list devices available 2>/dev/null | grep -i "iPhone" | grep -oE "[A-F0-9-]{36}" | head -1)
        if [ -n "$simulator" ]; then
            echo "Booting simulator $simulator..." >&2
            xcrun simctl boot "$simulator"
            sleep 5
            open -a Simulator 2>/dev/null || true
        fi
    fi
    echo "$simulator"
}

launch_app() {
    local build_dir app_path simulator
    build_dir=$(get_build_directory)
    app_path="${build_dir}/Kordant.app"
    simulator=$(ensure_simulator_booted)

    if [ -z "$simulator" ]; then
        echo "Error: No iOS simulator available. Boot one with: open -a Simulator"
        exit 1
    fi

    if [ -d "$app_path" ]; then
        echo "Installing on simulator $simulator..."
        xcrun simctl install "$simulator" "$app_path"
        echo "Launching app..."
        xcrun simctl launch "$simulator" "$BUNDLE_ID"
        sleep 2
        echo "Streaming simulator logs (Ctrl+C to stop - app keeps running)..."
        echo "================================================================"
        xcrun simctl spawn "$simulator" log stream --level debug \
            --predicate "subsystem contains \"$APP_SUBSYSTEM\"" \
            --style compact 2>/dev/null
    else
        echo "App not found at expected location, trying fallback..."
        local fallback
        fallback=$(ls -dt "$HOME/Library/Developer/Xcode/DerivedData/Kordant-"*/Build/Products/Debug-iphonesimulator/Lendair.app 2>/dev/null | head -1)
        if [ -d "$fallback" ]; then
            echo "Found at: $fallback"
            xcrun simctl install "$simulator" "$fallback"
            xcrun simctl launch "$simulator" "$BUNDLE_ID"
            sleep 2
            xcrun simctl spawn "$simulator" log stream --level debug \
                --predicate "subsystem contains \"$APP_SUBSYSTEM\"" \
                --style compact 2>/dev/null
        else
            echo "No app bundle found"
            exit 1
        fi
    fi
}

run_with_output() {
    local cmd="$1"
    local exit_code

    if [ "$VERBOSE" = true ] && [ -n "$OUTPUT_FILE" ]; then
        COMMAND_OUTPUT=$(eval "$cmd" 2>&1 | tee "$OUTPUT_FILE")
        exit_code=${PIPESTATUS[0]}
    elif [ "$VERBOSE" = true ]; then
        if [ -t 1 ]; then
            COMMAND_OUTPUT=$(eval "$cmd" 2>&1 | tee /dev/tty)
            exit_code=${PIPESTATUS[0]}
        else
            COMMAND_OUTPUT=$(eval "$cmd" 2>&1)
            exit_code=$?
            echo "$COMMAND_OUTPUT"
        fi
    elif [ -n "$OUTPUT_FILE" ]; then
        COMMAND_OUTPUT=$(eval "$cmd" 2>&1 | tee "$OUTPUT_FILE")
        exit_code=${PIPESTATUS[0]}
    else
        COMMAND_OUTPUT=$(eval "$cmd" 2>&1)
        exit_code=$?
    fi

    return $exit_code
}

show_usage() {
    echo "Usage: $0 [build|test|run|lsp] [-v|--verbose] [-o|--output <file_name>] [--no-lsp] [-p|--performance] [-c|--coverage]"
    echo ""
    echo "Commands:"
    echo "  build   - Build the application"
    echo "  test    - Run unit tests"
    echo "  run     - Build and run the application on simulator with logging (default)"
    echo "  launch  - Launch last-built app on booted simulator"
    echo "  lsp     - Update LSP configuration only (buildServer.json)"
    echo ""
    echo "Options:"
    echo "  -v, --verbose    - Show output in stdout"
    echo "  -o, --output     - Write output to log file"
    echo "  --no-lsp         - Skip LSP configuration update"
    echo "  -p, --performance - Run tests with performance profiling"
    echo "  -c, --coverage   - Run tests with code coverage analysis"
    echo ""
    echo "Note: Running './run' with no arguments defaults to 'run' action with verbose logging."
    echo "      Press Ctrl+C to stop log capture and keep the app running."
}

ACTION=""
while [[ $# -gt 0 ]]; do
    case $1 in
        -v|--verbose)
            VERBOSE=true
            shift
            ;;
        -o|--output)
            OUTPUT_FILE="$2"
            VERBOSE=true
            shift 2
            ;;
        --no-lsp)
            SKIP_LSP=true
            shift
            ;;
        -p|--performance)
            PERFORMANCE=true
            shift
            ;;
        -c|--coverage)
            COVERAGE=true
            shift
            ;;
        *)
            if [ -z "$ACTION" ]; then
                ACTION="$1"
            fi
            shift
            ;;
    esac
done

if [ -z "$ACTION" ]; then
    ACTION="run"
fi

echo "=== Kordant Application Script ==="

case "$ACTION" in
    build)
        echo "Building Kordant project..."
        run_with_output "$(build_xcodebuild_command build)"

        if [ $? -eq 0 ]; then
            handle_build_success
            echo "The app is located at: $(get_build_directory)/Kordant.app"

            if [ "$VERBOSE" = true ]; then
                print_warnings "$COMMAND_OUTPUT"
            fi
        else
            echo "Build failed!"
            print_errors "$COMMAND_OUTPUT" "Build"
            exit 1
        fi
        ;;

    test)
        echo "Running unit tests (parallel)..."
        run_with_output "$(build_xcodebuild_command test "platform=iOS Simulator,name=iPhone 16" "-parallel-testing-enabled YES")"

        if [ $? -eq 0 ]; then
            echo "Tests passed!"

            if [ "$VERBOSE" = true ]; then
                print_warnings "$COMMAND_OUTPUT"
            fi
        else
            echo "Tests failed!"
            print_errors "$COMMAND_OUTPUT" "Test"
            exit 1
        fi
        ;;

    run)
        echo "Building and running Kordant application..."

        kill_existing_lendair_processes

        run_with_output "$(build_xcodebuild_command build "generic/platform=iOS Simulator")"

        if [ $? -eq 0 ]; then
            handle_build_success

            if [ "$VERBOSE" = true ]; then
                print_warnings "$COMMAND_OUTPUT"
            fi

            launch_app
        else
            echo "Build failed!"
            print_errors "$COMMAND_OUTPUT" "Build"
            exit 1
        fi
        ;;

    lsp)
        echo "Updating LSP configuration only..."
        update_lsp_config
        ;;

    launch)
        echo "Launching last-built app on simulator..."
        launch_app
        ;;

    *)
        show_usage
        exit 1
        ;;
esac
