#!/bin/bash
# Install Unreal MCP and connect it to VS Code (via Continue MCP) on Arch Linux
# - Installs deps: git, jq, uv, python
# - Clones https://github.com/chongdashu/unreal-mcp
# - Creates a launcher: ~/.local/bin/unreal-mcp-server
# - Configures VS Code Continue MCP: ~/.continue/config.json
# - Optional: copies UnrealMCP plugin into a specified .uproject's Plugins/

set -euo pipefail

SCRIPT_NAME="$(basename "$0")"

# Source common library for shared functions
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=../../lib/common.sh
source "$SCRIPT_DIR/../../lib/common.sh"

# ---------- User/paths ----------
set_actual_user_vars

INSTALL_ROOT_DEFAULT="$USER_HOME/.local/share/unreal-mcp"
INSTALL_ROOT="$INSTALL_ROOT_DEFAULT"
REPO_URL="https://github.com/chongdashu/unreal-mcp.git"
REPO_DIR="" # will be set after INSTALL_ROOT known

PROJECT_UPROJECT=""     # optional: path to .uproject
RESOLVED_PROJECT_DIR="" # directory containing the resolved .uproject
CONFIGURE_CONTINUE=true
CONFIGURE_VSCODE_USER=true
FORCE_UPDATE=false

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
fail() {
  echo "[ERROR] $*" >&2
  exit 1
}

usage() {
  cat << EOF
Usage: $SCRIPT_NAME [options]

Options:
  --install-dir DIR        Install root for repo (default: $INSTALL_ROOT_DEFAULT)
  --project PATH           Path to your Unreal project (.uproject file) or a directory containing one
                           Copies UnrealMCP plugin into this Unreal project
  --no-continue            Skip configuring VS Code Continue MCP
  --no-vscode              Skip adding MCP server to VS Code user profile via --add-mcp
  --force-update           If repo exists, fetch and reset to origin/main
  -h, --help               Show this help

Examples:
  $SCRIPT_NAME --project ~/UnrealProjects/MyGame/MyGame.uproject
  $SCRIPT_NAME --install-dir "$USER_HOME/dev/unreal-mcp"
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --install-dir)
      shift
      [[ $# -gt 0 ]] || fail "--install-dir requires a value"
      INSTALL_ROOT="$1"
      ;;
    --project)
      shift
      [[ $# -gt 0 ]] || fail "--project requires a path to .uproject"
      PROJECT_UPROJECT="$1"
      ;;
    --no-continue)
      CONFIGURE_CONTINUE=false
      ;;
    --no-vscode)
      CONFIGURE_VSCODE_USER=false
      ;;
    --force-update)
      FORCE_UPDATE=true
      ;;
    -h | --help)
      usage
      exit 0
      ;;
    *)
      fail "Unknown option: $1"
      ;;
  esac
  shift
done

REPO_DIR="$INSTALL_ROOT/unreal-mcp"

# ---------- Dependencies ----------
require_cmd() { command -v "$1" > /dev/null 2>&1; }

ensure_packages_arch() {
  # Install with pacman using sudo when needed; keep idempotent with --needed
  local pkgs=(git jq uv python rsync)
  local to_install=()
  for p in "${pkgs[@]}"; do
    if ! pacman -Qi "$p" > /dev/null 2>&1; then
      to_install+=("$p")
    fi
  done
  if [[ ${#to_install[@]} -gt 0 ]]; then
    log "Installing packages: ${to_install[*]}"
    if [[ $EUID -eq 0 ]]; then
      pacman -S --noconfirm --needed "${to_install[@]}"
    else
      sudo pacman -S --noconfirm --needed "${to_install[@]}"
    fi
  else
    log "All required packages already installed"
  fi
}

check_python_version() {
  if require_cmd python; then
    local v
    v=$(python -V 2>&1 | awk '{print $2}')
  elif require_cmd python3; then
    local v
    v=$(python3 -V 2>&1 | awk '{print $2}')
  else
    log "python not found; pacman install will provide it"
    return 0
  fi
  # Require >= 3.12 (Unreal MCP docs)
  local major minor
  major=$(echo "$v" | cut -d. -f1)
  minor=$(echo "$v" | cut -d. -f2)
  if ((major < 3 || (major == 3 && minor < 12))); then
    log "Python $v detected; installing newer python via pacman"
    if [[ $EUID -eq 0 ]]; then
      pacman -S --noconfirm --needed python
    else
      sudo pacman -S --noconfirm --needed python
    fi
  fi
}

# ---------- Git clone/update ----------
setup_repo() {
  mkdir -p "$INSTALL_ROOT"
  if [[ ! -d "$REPO_DIR/.git" ]]; then
    log "Cloning unreal-mcp into $REPO_DIR"
    if require_cmd git; then
      git clone "$REPO_URL" "$REPO_DIR"
    else
      fail "git is required but not found after install"
    fi
  else
    log "Repo exists at $REPO_DIR"
    if [[ $FORCE_UPDATE == true ]]; then
      log "Updating repo with --force-update"
      git -C "$REPO_DIR" fetch origin
      git -C "$REPO_DIR" reset --hard origin/main
      git -C "$REPO_DIR" pull --rebase --autostash
    else
      log "Pulling latest changes"
      git -C "$REPO_DIR" pull --rebase --autostash
    fi
  fi

  # Ensure ownership for the real user when script ran via sudo
  if [[ $EUID -eq 0 ]]; then
    chown -R "$ACTUAL_USER:$ACTUAL_USER" "$INSTALL_ROOT"
  fi
}

# ---------- Launcher ----------
install_launcher() {
  local bin_dir="$USER_HOME/.local/bin"
  local python_dir="$REPO_DIR/Python"
  local launcher="$bin_dir/unreal-mcp-server"
  mkdir -p "$bin_dir"
  cat > "$launcher" << EOF
#!/bin/bash
set -euo pipefail
exec uv --directory "$python_dir" run unreal_mcp_server.py "\${1:-}" < /dev/null
EOF
  chmod +x "$launcher"
  if [[ $EUID -eq 0 ]]; then chown "$ACTUAL_USER:$ACTUAL_USER" "$launcher"; fi
  log "Installed launcher: $launcher"
}

# ---------- VS Code: Continue MCP config ----------
configure_continue() {
  if [[ $CONFIGURE_CONTINUE != true ]]; then
    log "Skipping Continue config (--no-continue)"
    return 0
  fi

  local cont_dir="$USER_HOME/.continue"
  local cont_cfg="$cont_dir/config.json"
  local python_dir="$REPO_DIR/Python"
  mkdir -p "$cont_dir"

  # Base JSON when no config exists
  local tmp_file
  tmp_file="$(mktemp)"
  if [[ ! -f $cont_cfg ]]; then
    cat > "$tmp_file" << JSON
{
  "mcpServers": {
    "unrealMCP": {
      "command": "uv",
      "args": ["--directory", "$python_dir", "run", "unreal_mcp_server.py"]
    }
  }
}
JSON
    mv "$tmp_file" "$cont_cfg"
  else
    # Merge using jq: ensure .mcpServers exists, then set/overwrite unrealMCP
    if ! require_cmd jq; then
      fail "jq is required to merge ~/.continue/config.json"
    fi
    jq --arg dir "$python_dir" '
      .mcpServers = (.mcpServers // {}) |
      .mcpServers.unrealMCP = {
        command: "uv",
        args: ["--directory", $dir, "run", "unreal_mcp_server.py"]
      }
    ' "$cont_cfg" > "$tmp_file" && mv "$tmp_file" "$cont_cfg"
  fi

  if [[ $EUID -eq 0 ]]; then chown "$ACTUAL_USER:$ACTUAL_USER" "$cont_cfg"; fi
  log "Configured Continue MCP at: $cont_cfg"
}

# ---------- VS Code user MCP (native) ----------
configure_vscode_user_mcp() {
  if [[ $CONFIGURE_VSCODE_USER != true ]]; then
    log "Skipping VS Code user MCP config (--no-vscode)"
    return 0
  fi

  if ! require_cmd jq; then
    fail "jq is required to compose VS Code --add-mcp JSON and to parse profiles"
  fi

  local python_dir="$REPO_DIR/Python"
  local json
  json=$(jq -n --arg dir "$python_dir" '{name:"unrealMCP", command:"uv", args:["--directory", $dir, "run", "unreal_mcp_server.py"]}')

  # Handle multiple VS Code variants if present
  local candidates=(code code-insiders codium)
  local found_any=false
  for cli in "${candidates[@]}"; do
    if ! command -v "$cli" > /dev/null 2>&1; then
      continue
    fi
    found_any=true
    log "Registering MCP server in VS Code user profile via: $cli --add-mcp"
    if "$cli" --add-mcp "$json" > "/tmp/${cli}-add-mcp.log" 2>&1; then
      log "[$cli] user profile: unrealMCP added/updated"
    else
      sed -n '1,200p' "/tmp/${cli}-add-mcp.log" || true
      fail "[$cli] --add-mcp failed for user profile. Ensure your VS Code supports MCP or rerun with --no-vscode."
    fi

    # Detect profiles with 'unreal' (case-insensitive) and add there too
    local data_dir=""
    case "$cli" in
      code)
        data_dir="$USER_HOME/.config/Code"
        ;;
      code-insiders)
        data_dir="$USER_HOME/.config/Code - Insiders"
        ;;
      codium)
        data_dir="$USER_HOME/.config/VSCodium"
        ;;
    esac
    local profiles_json="$data_dir/User/profiles/profiles.json"
    if [[ -f $profiles_json ]]; then
      # Extract profile names matching /unreal/i
      mapfile -t unreal_profiles < <(jq -r '.profiles // [] | .[] | .name // empty | select(test("unreal"; "i"))' "$profiles_json")
      if [[ ${#unreal_profiles[@]} -gt 0 ]]; then
        log "[$cli] Found profiles with 'unreal': ${unreal_profiles[*]}"
        local name
        for name in "${unreal_profiles[@]}"; do
          log "[$cli] Adding unrealMCP to profile: $name"
          if "$cli" --profile "$name" --add-mcp "$json" > "/tmp/${cli}-add-mcp-${name// /_}.log" 2>&1; then
            log "[$cli] profile '$name': unrealMCP added/updated"
          else
            sed -n '1,200p' "/tmp/${cli}-add-mcp-${name// /_}.log" || true
            fail "[$cli] --add-mcp failed for profile '$name'."
          fi
        done
      else
        log "[$cli] No VS Code profiles with 'unreal' in name"
      fi
    else
      log "[$cli] Profiles file not found: $profiles_json (skipping profile-specific adds)"
    fi
  done

  if [[ $found_any == false ]]; then
    fail "VS Code CLI not found (code/code-insiders/codium). Install VS Code and ensure 'code' CLI is available, or run with --no-vscode to skip."
  fi
}

# ---------- Unreal Plugin copy (optional) ----------
install_plugin_into_project() {
  [[ -n $PROJECT_UPROJECT ]] || return 0
  local upath="$PROJECT_UPROJECT"
  if [[ -d $upath ]]; then
    # Resolve .uproject in the provided directory
    mapfile -t _uprojects < <(find "$upath" -maxdepth 1 -type f -name "*.uproject" 2> /dev/null || true)
    if [[ ${#_uprojects[@]} -eq 0 ]]; then
      fail "--project directory '$upath' contains no .uproject files"
    elif [[ ${#_uprojects[@]} -gt 1 ]]; then
      printf '[ERROR] Multiple .uproject files found in %s:\n' "$upath" >&2
      printf '  - %s\n' "${_uprojects[@]}" >&2
      fail "Please pass the specific .uproject path to --project"
    else
      upath="${_uprojects[0]}"
      log "Resolved .uproject: $upath"
    fi
  elif [[ -f $upath ]]; then
    true
  else
    fail "--project path does not exist: $upath"
  fi
  if [[ ${upath##*.} != "uproject" ]]; then
    fail "--project must point to a .uproject file (got: $upath)"
  fi
  local proj_dir
  proj_dir="$(cd "$(dirname "$upath")" && pwd)"
  RESOLVED_PROJECT_DIR="$proj_dir"
  local src_plugin="$REPO_DIR/MCPGameProject/Plugins/UnrealMCP"
  local dst_plugin="$proj_dir/Plugins/UnrealMCP"
  if [[ ! -d $src_plugin ]]; then
    fail "Source plugin not found at $src_plugin (did repo layout change?)"
  fi
  mkdir -p "$proj_dir/Plugins"
  log "Copying UnrealMCP plugin to project: $dst_plugin"
  rsync -a --delete "$src_plugin/" "$dst_plugin/"
  # Set ownership back to actual user if run as root
  if [[ $EUID -eq 0 ]]; then chown -R "$ACTUAL_USER:$ACTUAL_USER" "$proj_dir/Plugins"; fi
  log "Plugin installed. Enable it from Unreal Editor (Edit > Plugins) if needed."
}

# ---------- Summary ----------
print_summary() {
  local python_dir="$REPO_DIR/Python"
  local plugin_dest="N/A"
  if [[ -n $RESOLVED_PROJECT_DIR ]]; then
    plugin_dest="$RESOLVED_PROJECT_DIR/Plugins/UnrealMCP"
  fi
  cat << EOF
============================================
Unreal MCP setup complete
============================================

Repo:        $REPO_DIR
Python dir:  $python_dir
Launcher:    $USER_HOME/.local/bin/unreal-mcp-server

VS Code (Continue) MCP configured: ${CONFIGURE_CONTINUE}
  - File: $USER_HOME/.continue/config.json
  - Server ID: unrealMCP

VS Code (User profile) MCP configured: ${CONFIGURE_VSCODE_USER}
  - Command used: code --add-mcp '{"name":"unrealMCP", "command":"uv", "args":["--directory","$python_dir","run","unreal_mcp_server.py"]}'

Optional usage:
  - Run server manually: unreal-mcp-server
  - In VS Code with Continue installed, the unrealMCP server will auto-start when needed.

Unreal plugin:
  - Source: MCPGameProject/Plugins/UnrealMCP
  - If you provided --project, the plugin was copied to: $plugin_dest
  - In the Unreal Editor: Edit > Plugins > search "UnrealMCP" and enable. Restart when prompted.

Notes:
  - Ensure you have Unreal Engine 5.5+ installed.
  - The Python server listens to the Unreal plugin on TCP port 55557 by default.
  - For other MCP clients (Claude Desktop, Cursor, Windsurf), copy the JSON snippet from the repo README to their config locations.
EOF
}

main() {
  log "Installing prerequisites (Arch Linux)"
  ensure_packages_arch
  check_python_version
  setup_repo
  install_launcher
  configure_continue
  install_plugin_into_project
  configure_vscode_user_mcp
  print_summary
}

main "$@"
