#!/bin/bash

set -e

# Function to display usage
usage() {
    echo "Usage: $0 --app-name <APP_NAME> --port <PORT> --app-dir <APP_DIR>"
    exit 1
}

# Load defaults from xbp.json if not provided
load_defaults() {
    if [[ -z "$APP_NAME" || -z "$PORT" || -z "$APP_DIR" ]]; then
        if [[ -f "./xbp.json" ]]; then
            APP_NAME="${APP_NAME:-$(jq -r '.project_name' ./xbp.json)}"
            PORT="${PORT:-$(jq -r '.port' ./xbp.json)}"
            APP_DIR="${APP_DIR:-$(jq -r '.build_dir' ./xbp.json)}"
        fi
    fi
}

# Check if port is in use
is_port_in_use() {
    local port=$1
    if command -v ss &> /dev/null; then
        ss -tuln | grep -q ":${port} "
    elif command -v netstat &> /dev/null; then
        netstat -tuln | grep -q ":${port} "
    else
        lsof -i ":${port}" &> /dev/null
    fi
}

# Kill all processes on a port aggressively
kill_port() {
    local port=$1
    echo "Killing processes on port $port..."

    # Method 1: fuser (most reliable on Linux)
    if command -v fuser &> /dev/null; then
        fuser -k "${port}/tcp" 2>/dev/null || true
    fi

    # Method 2: lsof + kill
    if command -v lsof &> /dev/null; then
        local pids=$(lsof -t -i ":${port}" 2>/dev/null || true)
        if [[ -n "$pids" ]]; then
            echo "Killing PIDs: $pids"
            echo "$pids" | xargs -r kill -9 2>/dev/null || true
        fi
    fi

    # Method 3: ss + kill (fallback)
    if command -v ss &> /dev/null; then
        local pids=$(ss -tlnp "sport = :${port}" 2>/dev/null | grep -oP 'pid=\K[0-9]+' || true)
        if [[ -n "$pids" ]]; then
            echo "Killing PIDs from ss: $pids"
            echo "$pids" | xargs -r kill -9 2>/dev/null || true
        fi
    fi

    # Method 4: XBP
    xbp ports -p "$port" --kill 2>/dev/null || true
}

# Wait for port to be free with retries
wait_for_port_free() {
    local port=$1
    local max_attempts=10
    local attempt=1
    local wait_time=1

    echo "Waiting for port $port to be free..."

    while is_port_in_use "$port"; do
        if [[ $attempt -ge $max_attempts ]]; then
            echo "ERROR: Port $port still in use after $max_attempts attempts"
            echo "Processes still using port:"
            lsof -i ":${port}" 2>/dev/null || ss -tlnp "sport = :${port}" 2>/dev/null || true
            return 1
        fi

        echo "  Attempt $attempt/$max_attempts: Port $port still in use, killing again..."
        kill_port "$port"
        sleep "$wait_time"

        # Exponential backoff (1, 2, 3, 4, 5 seconds)
        wait_time=$((wait_time < 5 ? wait_time + 1 : 5))
        attempt=$((attempt + 1))
    done

    echo "Port $port is free"
    return 0
}

# Wait for service to be healthy
wait_for_healthy() {
    local port=$1
    local max_attempts=30
    local attempt=1

    echo "Waiting for service to be healthy on port $port..."

    while [[ $attempt -le $max_attempts ]]; do
        if curl -sf "http://localhost:${port}/health" > /dev/null 2>&1; then
            echo "Service is healthy after $attempt attempts"
            return 0
        fi

        echo "  Attempt $attempt/$max_attempts: Service not ready yet..."
        sleep 1
        attempt=$((attempt + 1))
    done

    echo "WARNING: Service health check failed after $max_attempts attempts"
    return 1
}

# Parse CLI arguments
while [[ "$#" -gt 0 ]]; do
    case "$1" in
    --app-name)
        APP_NAME="$2"
        shift 2
        ;;
    --port)
        PORT="$2"
        shift 2
        ;;
    --app-dir)
        APP_DIR="$2"
        shift 2
        ;;
    *)
        usage
        ;;
    esac
done

# Load defaults if necessary
load_defaults

# Validate inputs
if [[ -z "$APP_NAME" || -z "$PORT" || -z "$APP_DIR" ]]; then
    usage
fi

# Redeploy $APP_NAME
echo "========================================"
echo "Redeploying $APP_NAME on port $PORT"
echo "========================================"

cd "$APP_DIR" || {
    echo "Failed to navigate to $APP_DIR directory"
    exit 1
}

echo "Resetting local changes for $APP_NAME ..."
git reset --hard || {
    echo "Failed to reset local changes for $APP_NAME "
    exit 1
}

echo "Pulling latest changes for $APP_NAME ..."
git pull origin main || {
    echo "Failed to pull latest changes for $APP_NAME "
    exit 1
}

echo "Building $APP_NAME ..."
cargo build --release || {
    echo "Failed to build $APP_NAME "
    exit 1
}

echo "Generating docs for $APP_NAME ..."
cargo d || {
    echo "Failed to generate docs for $APP_NAME "
    exit 1
}

echo "Stopping existing PM2 process for $APP_NAME ..."
pm2 stop "$APP_NAME" 2>/dev/null || echo "No existing PM2 process found for $APP_NAME. Proceeding..."
pm2 delete "$APP_NAME" 2>/dev/null || true

cp ./target/release/athena_rs ./athena_rs || {
    echo "Failed to copy binary"
    exit 1
}

echo "Killing any processes on port $PORT..."
kill_port "$PORT"

if ! wait_for_port_free "$PORT"; then
    echo "FATAL: Cannot free port $PORT, aborting deployment"
    exit 1
fi

echo "Starting $APP_NAME with PM2..."
pm2 start "./athena_rs" --name "$APP_NAME" -- --api-only --port "$PORT" || {
    echo "Failed to start $APP_NAME"
    exit 1
}

# Give PM2 a moment to spawn the process
sleep 2

# Verify the service started correctly
if ! wait_for_healthy "$PORT"; then
    echo "Service may not be fully healthy, checking PM2 status..."
    pm2 status "$APP_NAME"
fi

echo "Saving PM2 process list for $APP_NAME ..."
pm2 save || {
    echo "Failed to save PM2 process list for $APP_NAME "
    exit 1
}

echo "========================================"
echo "Deployment complete for $APP_NAME"
echo "========================================"
