#!/bin/bash
# dx - Quick dev environment launcher
#
# Usage:
#   dx                   # Shell in dev container for current project
#   dx claude            # Run Claude Code in dev container
#   dx <command>         # Run any command in dev container
#   dx --info            # Show detected project info
#   dx --shell           # Force shell (even if command specified)
#
# Auto-detects:
#   - Python (pyproject.toml, setup.py, requirements.txt)
#   - Node.js (package.json)
#   - Rust (Cargo.toml)
#   - Go (go.mod)
#   - devcontainer.json (uses VS Code devcontainer config)
#
# Containers are named after the project directory and persist.
# Project is bound at the SAME absolute path for Claude Code resume support.

set -e

# Resolve symlinks to find actual script location
SCRIPT_PATH="${BASH_SOURCE[0]}"
while [[ -L "$SCRIPT_PATH" ]]; do
    SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
    SCRIPT_PATH="$(readlink "$SCRIPT_PATH")"
    [[ "$SCRIPT_PATH" != /* ]] && SCRIPT_PATH="$SCRIPT_DIR/$SCRIPT_PATH"
done
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
DEV_SH="$SCRIPT_DIR/dev.sh"

# Colors
CYAN='\033[0;36m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
DIM='\033[2m'
NC='\033[0m'

log_info() { echo -e "${BLUE}[dx]${NC} $1"; }
log_success() { echo -e "${GREEN}[dx]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[dx]${NC} $1"; }
log_error() { echo -e "${RED}[dx]${NC} $1"; }
log_dim() { echo -e "${DIM}$1${NC}"; }

# Get project root (look for git root or stay in cwd)
get_project_root() {
    local dir
    dir=$(git rev-parse --show-toplevel 2>/dev/null) && echo "$dir" && return
    pwd
}

# Detect project type and return two lines:
# Line 1: detected types (human-readable, comma-separated)
# Line 2: overlays (space-separated, for dev.sh)
detect_project_type() {
    local project_dir="$1"
    local overlays=()
    local detected=""

    # Always include claude overlay for ai-assisted development
    overlays+=("claude")

    # Check for devcontainer.json first
    if [[ -f "$project_dir/.devcontainer/devcontainer.json" ]] || [[ -f "$project_dir/.devcontainer.json" ]]; then
        detected="devcontainer"
    fi

    # Python
    if [[ -f "$project_dir/pyproject.toml" ]] || \
       [[ -f "$project_dir/setup.py" ]] || \
       [[ -f "$project_dir/requirements.txt" ]] || \
       [[ -f "$project_dir/Pipfile" ]]; then
        overlays+=("python")
        detected="${detected:+$detected, }python"
    fi

    # Node.js
    if [[ -f "$project_dir/package.json" ]]; then
        overlays+=("node")
        detected="${detected:+$detected, }node"
    fi

    # Rust
    if [[ -f "$project_dir/Cargo.toml" ]]; then
        overlays+=("rust")
        detected="${detected:+$detected, }rust"
    fi

    # Go
    if [[ -f "$project_dir/go.mod" ]]; then
        overlays+=("go")
        detected="${detected:+$detected, }go"
    fi

    # Tauri (requires rust + node)
    if [[ -f "$project_dir/src-tauri/Cargo.toml" ]] || [[ -f "$project_dir/tauri.conf.json" ]]; then
        overlays+=("tauri")
        detected="${detected:+$detected, }tauri"
    fi

    # Add shell-tools for all dev containers
    overlays+=("shell-tools")

    # Output: line 1 = detected types, line 2 = overlays
    echo "$detected"
    printf '%s\n' "${overlays[@]}" | sort -u | tr '\n' ' '
    echo  # trailing newline for overlays line
}

# Get container name for a project
get_container_name() {
    local project_dir="$1"
    local basename
    basename=$(basename "$project_dir")
    # Sanitize: lowercase, replace spaces/special chars with hyphen
    echo "dev-$basename" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g'
}

# Check if container exists
container_exists() {
    machinectl show-image "$1" &>/dev/null
}

# Check if container is running
is_running() {
    machinectl show "$1" --property=State 2>/dev/null | grep -q "State=running"
}

# Ensure dev template exists
ensure_template() {
    if ! machinectl show-image "dev-template" &>/dev/null; then
        log_info "Creating dev base template (one-time setup)..."
        "$DEV_SH" template
    fi
}

# Ensure container exists and is configured for this project
ensure_container() {
    local name="$1"
    local project_dir="$2"
    shift 2
    local overlays=("$@")

    if container_exists "$name"; then
        log_dim "Container $name exists"

        # Check if project is already bound
        local config="/etc/systemd/nspawn/$name.nspawn"
        if ! grep -q "Bind=$project_dir:$project_dir" "$config" 2>/dev/null; then
            log_info "Binding project directory..."
            # Add project binding at same path
            sudo bash -c "echo 'Bind=$project_dir:$project_dir' >> $config"
            # Restart if running to apply
            if is_running "$name"; then
                log_info "Restarting container to apply binding..."
                sudo machinectl stop "$name"
                sleep 2
            fi
        fi
    else
        log_info "Creating container $name with overlays: ${overlays[*]}"
        ensure_template

        # Create container with detected overlays
        "$DEV_SH" create "${name#dev-}" "${overlays[@]}"

        # Add project binding at same absolute path
        local config="/etc/systemd/nspawn/$name.nspawn"
        sudo bash -c "echo 'Bind=$project_dir:$project_dir' >> $config"
    fi
}

# Start container if not running
ensure_running() {
    local name="$1"

    if ! is_running "$name"; then
        log_info "Starting $name..."
        sudo machinectl start "$name"
        sleep 3
    fi
}

# Set terminal title (works for both regular terminals and tmux)
set_title() {
    local title="$1"
    # Standard terminal title
    printf '\033]0;%s\007' "$title"
    # Tmux: use rename-window command (works even with allow-rename off)
    if [[ -n "$TMUX" ]]; then
        tmux rename-window "$title"
    else
        # Fallback: tmux escape sequence
        printf '\033k%s\033\\' "$title"
    fi
}

# Execute command in container with proper PTY
exec_in_container() {
    local name="$1"
    local project_dir="$2"
    shift 2
    local cmd=("$@")

    if [[ ${#cmd[@]} -eq 0 ]]; then
        # Set terminal title for shell
        set_title "dx: $name"
        # Interactive shell - use machinectl shell directly as dev user
        sudo machinectl shell --setenv=TERM="$TERM" dev@"$name" /bin/bash -c "cd '$project_dir' && exec bash -l"
    else
        # Set terminal title for command
        set_title "dx: ${cmd[0]}"
        # Run command with login shell to pick up PATH from .bashrc
        local cmd_str="${cmd[*]}"
        sudo machinectl shell --setenv=TERM="$TERM" dev@"$name" /bin/bash -l -c "cd '$project_dir' && $cmd_str"
    fi
}

# Upgrade container with latest Claude and bindings
upgrade_container() {
    local name="$1"

    if ! container_exists "$name"; then
        log_error "Container $name does not exist"
        exit 1
    fi

    log_info "Upgrading $name..."

    # Start if needed
    ensure_running "$name"

    # Remove old npm installation if present
    log_info "Cleaning up old installations..."
    sudo machinectl shell root@"$name" /bin/bash -c '
        if command -v npm &>/dev/null && npm list -g @anthropic-ai/claude-code &>/dev/null 2>&1; then
            echo "Removing old npm installation..."
            npm uninstall -g @anthropic-ai/claude-code 2>/dev/null || true
        fi
    ' 2>/dev/null || true

    # Install/upgrade Claude Code using native installer
    log_info "Installing/upgrading Claude Code..."
    sudo machinectl shell dev@"$name" /bin/bash -c '
        curl -fsSL https://claude.ai/install.sh -o /tmp/install.sh && bash /tmp/install.sh
        grep -q "/.local/bin" ~/.bashrc || echo "export PATH=\"\$HOME/.local/bin:\$PATH\"" >> ~/.bashrc
        # Run update to ensure latest version
        ~/.local/bin/claude update 2>/dev/null || true
    '

    # Add .claude.json binding if missing
    local config="/etc/systemd/nspawn/$name.nspawn"
    if ! grep -q "claude.json" "$config" 2>/dev/null; then
        log_info "Adding .claude.json binding..."
        sudo bash -c "echo 'Bind=$HOME/.claude.json:/home/dev/.claude.json' >> $config"
    fi

    # Restart to apply any new bindings
    log_info "Restarting container..."
    sudo machinectl stop "$name"
    sleep 2
    sudo machinectl start "$name"
    sleep 2

    log_success "Upgrade complete for $name"
}

# Show project info
show_info() {
    local project_dir
    project_dir=$(get_project_root)
    local name
    name=$(get_container_name "$project_dir")

    echo ""
    echo -e "${CYAN}dx - dev environment info${NC}"
    echo ""
    echo -e "Project:     ${GREEN}$(basename "$project_dir")${NC}"
    echo -e "Path:        $project_dir"
    echo -e "Container:   $name"
    echo ""

    # Detect and show overlays (returns 2 lines: detected types, then overlays)
    local detection_output
    detection_output=$(detect_project_type "$project_dir")
    local detected
    detected=$(echo "$detection_output" | head -n1)
    local overlays
    overlays=$(echo "$detection_output" | tail -n1)

    echo -n "Detected:    "
    if [[ -n "$detected" ]]; then
        echo -e "${GREEN}$detected${NC}"
    else
        echo -e "${YELLOW}none (generic shell)${NC}"
    fi

    echo -e "Overlays:    $overlays"
    echo ""

    # Container status
    echo -n "Status:      "
    if container_exists "$name"; then
        if is_running "$name"; then
            echo -e "${GREEN}running${NC}"
        else
            echo -e "${YELLOW}stopped${NC}"
        fi
    else
        echo -e "${DIM}not created${NC}"
    fi

    echo ""
}

# Main
main() {
    local show_info_flag=false
    local force_shell=false
    local restart_flag=false
    local upgrade_flag=false
    local cmd=()

    # Parse arguments
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --info|-i)
                show_info_flag=true
                shift
                ;;
            --shell|-s)
                force_shell=true
                shift
                ;;
            --restart|-r)
                restart_flag=true
                shift
                ;;
            --upgrade|-u)
                upgrade_flag=true
                shift
                ;;
            --help|-h)
                echo "dx - Quick dev environment launcher"
                echo ""
                echo "Usage:"
                echo "  dx                   Open shell in dev container"
                echo "  dx claude            Run Claude Code in container"
                echo "  dx <cmd> [args]      Run command in container"
                echo ""
                echo "Options:"
                echo "  --info, -i           Show detected project info"
                echo "  --shell, -s          Force shell (even if cmd specified)"
                echo "  --restart, -r        Restart container before entering"
                echo "  --upgrade, -u        Upgrade Claude Code and bindings"
                echo "  --help, -h           Show this help"
                echo ""
                echo "Auto-detects project type from:"
                echo "  - pyproject.toml, setup.py, requirements.txt (Python)"
                echo "  - package.json (Node.js)"
                echo "  - Cargo.toml (Rust)"
                echo "  - go.mod (Go)"
                echo "  - .devcontainer/devcontainer.json (VS Code devcontainer)"
                echo ""
                echo "Containers persist between sessions. Project is bound at"
                echo "the same absolute path for Claude Code resume support."
                exit 0
                ;;
            --)
                shift
                cmd+=("$@")
                break
                ;;
            *)
                cmd+=("$1")
                shift
                ;;
        esac
    done

    # Show info and exit if requested
    if $show_info_flag; then
        show_info
        exit 0
    fi

    # Get container name for upgrade check
    local project_dir
    project_dir=$(get_project_root)
    local name
    name=$(get_container_name "$project_dir")

    # Upgrade and exit if requested
    if $upgrade_flag; then
        upgrade_container "$name"
        exit 0
    fi

    # Detect project type (returns 2 lines: detected types, then overlays)
    local detection_output
    detection_output=$(detect_project_type "$project_dir")
    local overlays
    overlays=$(echo "$detection_output" | tail -n1 | xargs)

    # Create array from space-separated overlays
    local overlay_array=()
    read -ra overlay_array <<< "$overlays"

    # Ensure container exists and is configured
    ensure_container "$name" "$project_dir" "${overlay_array[@]}"

    # Restart if requested
    if $restart_flag && is_running "$name"; then
        log_info "Restarting $name..."
        sudo machinectl stop "$name"
        sleep 2
    fi

    # Start if needed
    ensure_running "$name"

    # Determine what to run
    if $force_shell || [[ ${#cmd[@]} -eq 0 ]]; then
        log_success "Entering $name at $project_dir"
        exec_in_container "$name" "$project_dir"
    else
        log_dim "Running: ${cmd[*]}"
        exec_in_container "$name" "$project_dir" "${cmd[@]}"
    fi
}

main "$@"
