#!/bin/bash
# Run Chattyness servers in development mode
set -eu

cd "$(dirname "$0")"

# =============================================================================
# Constants
# =============================================================================
readonly APP_PORT=3000
readonly OWNER_PORT=3001
readonly LOCKFILE=".run-dev.lock"

# Process IDs (set during runtime)
OWNER_PID=""
APP_PID=""

# =============================================================================
# Helper Functions
# =============================================================================

usage() {
	cat <<EOF
Usage: $0 [build|watch] [--user-app|--owner-app|--both] [-f|-k|-s|-r]

Commands:
  build   Build and run servers (default)
  watch   Watch mode with hot reload

Options:
  --user-app    Run only the user app (port $APP_PORT)
  --owner-app   Run only the owner admin (port $OWNER_PORT)
  --both        Run both servers (default)
  -f, --force   Kill existing instance and start new one
  -k, --kill    Kill existing instance and exit
  -s, --status  Check if an instance is running
  -r, --release Build and run in release mode

Examples:
  $0                     # Build and run both
  $0 watch               # Watch mode for both
  $0 --owner-app         # Build and run owner only
  $0 watch --user-app    # Watch mode for user app only
  $0 -f watch            # Kill existing, then watch mode
  $0 -k                  # Kill existing instance
  $0 -s                  # Check status
  $0 -r                  # Build and run in release mode
EOF
	exit 1
}

# Target helper functions
run_owner() { [ "$TARGET" = "both" ] || [ "$TARGET" = "owner" ]; }
run_app() { [ "$TARGET" = "both" ] || [ "$TARGET" = "app" ]; }

# Cleanup function - kills child processes and removes lock file
cleanup() {
	echo ""
	echo "Shutting down..."
	[ -n "$OWNER_PID" ] && kill "$OWNER_PID" 2>/dev/null || true
	[ -n "$APP_PID" ] && kill "$APP_PID" 2>/dev/null || true
	[ -n "$OWNER_PID" ] && wait "$OWNER_PID" 2>/dev/null || true
	[ -n "$APP_PID" ] && wait "$APP_PID" 2>/dev/null || true
	rm -f "$LOCKFILE"
	echo "Done."
}

# Print server URLs based on what's running
print_server_info() {
	local mode="$1"
	local build_type="Debug"
	[ "$RELEASE" = "true" ] && build_type="Release"
	echo ""
	echo "========================================"
	echo "  Chattyness Development ($mode - $build_type)"
	echo "========================================"
	echo ""
	if run_owner; then
		echo "  Owner Admin: http://127.0.0.1:$OWNER_PORT"
	fi
	if run_app; then
		echo "  User App:    http://127.0.0.1:$APP_PORT"
		echo "  Realm Admin: http://127.0.0.1:$APP_PORT/admin"
	fi
	echo ""
	if [ "$TARGET" = "both" ]; then
		echo "Admin CSS lazy-loaded via symlink from owner build."
	fi
	echo "Press Ctrl+C to stop"
	echo ""
}

# Wait for running child processes
wait_for_processes() {
	if [ "$TARGET" = "both" ]; then
		wait "$OWNER_PID" "$APP_PID"
	elif [ "$TARGET" = "owner" ]; then
		wait "$OWNER_PID"
	else
		wait "$APP_PID"
	fi
}

# Create symlink for admin CSS (user app loads admin CSS from owner build)
setup_admin_css_symlink() {
	mkdir -p apps/chattyness-app/public
	local target="../../../target/site-owner/static/chattyness-owner.css"
	local link="apps/chattyness-app/public/admin.css"
	if [ ! -L "$link" ]; then
		ln -sf "$target" "$link"
		echo "Created symlink: $link -> $target"
	fi
}

# Load environment variables from .env file
load_env() {
	if [ -f .env ]; then
		set -a
		# shellcheck source=/dev/null
		source .env
		set +a
	fi
}

# Validate required environment variables
check_env() {
	if [ -z "${DB_CHATTYNESS_OWNER:-}" ]; then
		echo "Error: DB_CHATTYNESS_OWNER not set"
		echo "Create a .env file with DB_CHATTYNESS_OWNER=your_password"
		exit 1
	fi

	if [ -z "${DB_CHATTYNESS_APP:-}" ]; then
		echo "Error: DB_CHATTYNESS_APP not set"
		echo "Create a .env file with DB_CHATTYNESS_APP=your_password"
		exit 1
	fi
}

# =============================================================================
# Lock File Management
# =============================================================================

# Check if another instance is running (returns PID or empty)
get_running_pid() {
	if [ -f "$LOCKFILE" ]; then
		local pid
		pid=$(cat "$LOCKFILE" 2>/dev/null)
		if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
			echo "$pid"
		fi
	fi
}

# Handle --status flag
handle_status() {
	local pid
	pid=$(get_running_pid)
	if [ -n "$pid" ]; then
		echo "Running (PID: $pid)"
		exit 0
	else
		echo "Not running"
		[ -f "$LOCKFILE" ] && rm -f "$LOCKFILE" # Clean stale lock
		exit 1
	fi
}

# Kill any running instance (shared by -k and -f)
kill_running_instance() {
	local pid
	pid=$(get_running_pid)
	if [ -n "$pid" ]; then
		echo "Killing existing run-dev.sh (PID: $pid)..."
		kill "$pid"
	else
		echo "No running instance found."
		[ -f "$LOCKFILE" ] && rm -f "$LOCKFILE" # Clean stale lock
	fi
	echo "Killing chattyness processes..."
	killall -9 chattyness-app 2>/dev/null || true
	killall -9 chattyness-owner 2>/dev/null || true
	echo "Done."
}

# Handle --kill flag
handle_kill() {
	kill_running_instance
	exit 0
}

# Handle --force flag (kill existing, then continue)
handle_force() {
	kill_running_instance
	sleep 1 # Allow lock release
}

# Acquire exclusive lock
acquire_lock() {
	exec 200>"$LOCKFILE"
	if ! flock -n 200; then
		local pid
		pid=$(cat "$LOCKFILE" 2>/dev/null)
		echo ""
		echo "ERROR: Another run-dev.sh instance is already running (PID: $pid)"
		echo ""
		echo "Options:"
		echo "  $0 -k              Kill the existing instance"
		echo "  $0 -f [args...]    Kill existing and start new instance"
		echo "  $0 -s              Check status"
		echo ""
		exit 1
	fi
	echo $$ >"$LOCKFILE"
}

# =============================================================================
# Main Commands
# =============================================================================

do_watch() {
	echo "Starting watch mode..."
	echo ""

	local release_flag=""
	[ "$RELEASE" = "true" ] && release_flag="--release"

	# Build owner first to create CSS (needed if user app needs admin CSS)
	if run_owner; then
		echo "Building owner app first (for admin CSS)..."
		cargo leptos build -p chattyness-owner $release_flag
		setup_admin_css_symlink
	fi

	# Start watch processes
	if run_owner; then
		cargo leptos watch -p chattyness-owner $release_flag &
		OWNER_PID=$!
	fi

	if run_app; then
		cargo leptos watch -p chattyness-app --split $release_flag &
		APP_PID=$!
	fi

	print_server_info "Watch Mode"
	wait_for_processes
}

do_build() {
	local release_flag=""
	local target_dir="debug"
	if [ "$RELEASE" = "true" ]; then
		release_flag="--release"
		target_dir="release"
	fi

	# Build phase
	if run_owner; then
		echo "Running: cargo leptos build -p chattyness-owner $release_flag"
		cargo leptos build -p chattyness-owner $release_flag
		setup_admin_css_symlink
	fi

	if run_app; then
		echo ""
		echo "Running: cargo leptos build -p chattyness-app --split $release_flag"
		cargo leptos build -p chattyness-app --split $release_flag
	fi

	# Verify binaries exist
	if run_owner && [ ! -x "./target/$target_dir/chattyness-owner" ]; then
		echo "Error: ./target/$target_dir/chattyness-owner not found or not executable"
		exit 1
	fi
	if run_app && [ ! -x "./target/$target_dir/chattyness-app" ]; then
		echo "Error: ./target/$target_dir/chattyness-app not found or not executable"
		exit 1
	fi

	# Start servers
	if run_owner; then
		echo "Starting Owner Server on :$OWNER_PORT..."
		./target/$target_dir/chattyness-owner &
		OWNER_PID=$!
	fi

	if run_app; then
		echo "Starting App Server on :$APP_PORT..."
		./target/$target_dir/chattyness-app &
		APP_PID=$!
	fi

	print_server_info "Servers"
	wait_for_processes
}

# =============================================================================
# Main Entry Point
# =============================================================================

# Parse arguments
LEPTOS_CMD="build"
TARGET="both"
FORCE="false"
KILL_EXISTING="false"
CHECK_STATUS="false"
RELEASE="false"

for arg in "$@"; do
	case $arg in
	build | watch)
		LEPTOS_CMD="$arg"
		;;
	--user-app)
		TARGET="app"
		;;
	--owner-app)
		TARGET="owner"
		;;
	--both)
		TARGET="both"
		;;
	-f | --force)
		FORCE="true"
		;;
	-k | --kill)
		KILL_EXISTING="true"
		;;
	-s | --status)
		CHECK_STATUS="true"
		;;
	-r | --release)
		RELEASE="true"
		;;
	--help | -h)
		usage
		;;
	*)
		echo "Unknown option: $arg"
		usage
		;;
	esac
done

# Handle special flags first (before acquiring lock)
[ "$CHECK_STATUS" = "true" ] && handle_status
[ "$KILL_EXISTING" = "true" ] && handle_kill
[ "$FORCE" = "true" ] && handle_force

# Acquire lock and store PID
acquire_lock

# Set up cleanup trap
trap cleanup EXIT INT TERM

# Load and validate environment
load_env
check_env

# Run the requested command
case "$LEPTOS_CMD" in
watch) do_watch ;;
build) do_build ;;
esac
