#!/bin/sh
# Forge source/dev local app bootstrap for macOS/Linux.
# Signed native installers are not published yet. This script installs the
# current source/dev path and opens the real local Forge app UI.
set -eu
printf 'Forge is preparing your local AI operating system.\n'
printf 'From one command to local Forge app and AI agent runtime.\n'
FORGE_STEP_NUMBER=0
FORGE_INSTALLER_START_CWD="$(pwd)"

step() {
  FORGE_STEP_NUMBER=$((FORGE_STEP_NUMBER + 1))
  printf '\nStep %s - %s\n' "$FORGE_STEP_NUMBER" "$1"
}

command_exists() {
  command -v "$1" >/dev/null 2>&1
}

resolve_state_root() {
  RAW_STATE_ROOT="${FORGE_LOCAL_APP_STATE_DIR:-$HOME/.local/state/forge/local-app-control-room}"
  case "$RAW_STATE_ROOT" in
    /*) printf '%s\n' "$RAW_STATE_ROOT" ;;
    *) printf '%s\n' "$FORGE_INSTALLER_START_CWD/$RAW_STATE_ROOT" ;;
  esac
}

clone_repo() {
  CLONE_TARGET="$1"
  if git clone "$REPO_URL" "$CLONE_TARGET"; then
    return 0
  fi
  if [ -d "$CLONE_TARGET" ] && [ -z "$(find "$CLONE_TARGET" -mindepth 1 -maxdepth 1 -print -quit)" ]; then
    rmdir "$CLONE_TARGET"
  fi
  DEFAULT_HTTPS_REPO_URL="https://github.com/mastermers-spec/forge-v7.git"
  SSH_REPO_URL="${FORGE_SSH_REPO_URL:-git@github.com:mastermers-spec/forge-v7.git}"
  if [ "$REPO_URL" = "$DEFAULT_HTTPS_REPO_URL" ] && [ -n "$SSH_REPO_URL" ]; then
    echo "HTTPS clone did not complete. Trying SSH fallback: $SSH_REPO_URL" >&2
    if git clone "$SSH_REPO_URL" "$CLONE_TARGET"; then
      return 0
    fi
    if [ -d "$CLONE_TARGET" ] && [ -z "$(find "$CLONE_TARGET" -mindepth 1 -maxdepth 1 -print -quit)" ]; then
      rmdir "$CLONE_TARGET"
    fi
  fi
  echo "Forge source/dev checkout could not be cloned." >&2
  echo "Set FORGE_REPO_URL to an accessible Forge source or install from signed release artifacts when they are published." >&2
  return 128
}

write_install_artifacts() {
  STATE_ROOT="$(resolve_state_root)"
  mkdir -p "$STATE_ROOT"
  CREATED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  RECEIPT_PATH="$STATE_ROOT/install-receipt.json"
  PASSPORT_PATH="$STATE_ROOT/activation-passport.local.json"
  cat > "$RECEIPT_PATH" <<EOF
{
  "schema": "forge.local-first-control-room.install-receipt.v1",
  "status": "installed_source_dev_local_app",
  "createdAt": "$CREATED_AT",
  "installPath": "$INSTALL_ROOT",
  "openCommand": "npm run forge:local-first -- dev-app",
  "appUrl": "http://127.0.0.1:3417",
  "appUrlSource": "current-app.json",
  "launcherOpensActualAppUrl": true,
  "signedNativeInstallerStatus": "deferred_external_signing_artifacts",
  "localAppRequiresFullNpmInstall": false
}
EOF
  cat > "$PASSPORT_PATH" <<EOF
{
  "schema": "forge.local-first-control-room.activation-passport.v1",
  "status": "local_only_source_dev",
  "createdAt": "$CREATED_AT",
  "installPath": "$INSTALL_ROOT",
  "accountSignInRequired": false,
  "providerSecretsIncluded": false,
  "plainEnglish": "Forge can open locally now. Sign in later only when account or licensed capability is needed."
}
EOF
}

create_launcher() {
  LAUNCHER_PATH="$INSTALL_ROOT/open-forge-app.sh"
  ICON_PATH="$INSTALL_ROOT/forge-icon.svg"
  cat > "$ICON_PATH" <<'EOF'
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="Forge"><rect width="128" height="128" rx="28" fill="#17201b"/><path d="M28 84c18 16 48 16 66 0 9-8 14-19 14-31 0-11-4-21-12-29-1 19-14 29-27 36 1-14-4-27-17-39 0 20-9 31-20 42-6 6-9 12-9 20 0 1 0 2 1 3z" fill="#c6532b"/><path d="M43 89c9 8 28 8 39 0 5-4 8-10 8-17-8 8-18 11-30 11-9 0-16-2-24-7 1 5 3 10 7 13z" fill="#f3efe4"/></svg>
EOF
cat > "$LAUNCHER_PATH" <<'EOF'
#!/bin/sh
set -eu
INSTALL_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
cd "$INSTALL_ROOT"
STATE_ROOT="${FORGE_LOCAL_APP_STATE_DIR:-$HOME/.local/state/forge/local-app-control-room}"
mkdir -p "$STATE_ROOT"
CURRENT_APP_PATH="$STATE_ROOT/current-app.json"
PROOF_PATH="$STATE_ROOT/launcher-proof.json"
LOG_PATH="$STATE_ROOT/forge-app.log"
PID_PATH="$STATE_ROOT/forge-app.pid"
select_node_bin() {
  for candidate in "${FORGE_NODE_BIN:-}" "$(command -v node 2>/dev/null || true)" "$(command -v nodejs 2>/dev/null || true)"; do
    if [ -n "$candidate" ] && "$candidate" -e 'const major = Number(process.versions.node.split(".")[0]); process.exit(major >= 22 ? 0 : 1)' >/dev/null 2>&1; then
      printf '%s' "$candidate"
      return 0
    fi
  done
  if command -v npx >/dev/null 2>&1; then
    npx -y node@24 -p 'process.execPath'
    return 0
  fi
  return 1
}
current_app_url() {
  if [ -f "$CURRENT_APP_PATH" ]; then
    node -e 'try { const fs = require("fs"); const file = process.argv[1]; const data = JSON.parse(fs.readFileSync(file, "utf8")); if (data.url) process.stdout.write(data.url); } catch {}' "$CURRENT_APP_PATH"
  fi
}
url_reachable() {
  command -v curl >/dev/null 2>&1 && curl -fsS "$1" >/dev/null 2>&1
}
APP_URL=""
APP_URL_SOURCE=""
SERVER_STARTED=false
APP_READY=false
HAS_CURRENT_APP=false
DETECTED_URL="$(current_app_url || true)"
if [ -n "$DETECTED_URL" ]; then
  APP_URL="$DETECTED_URL"
  APP_URL_SOURCE="current-app.json"
  HAS_CURRENT_APP=true
fi
if [ "$HAS_CURRENT_APP" != true ] || ! url_reachable "$APP_URL"; then
  NODE_BIN="$(select_node_bin)" || {
    printf 'Forge needs Node.js 22 LTS or newer to launch the local app. Install Node.js 22 LTS and retry.\n' >&2
    exit 1
  }
  if command -v setsid >/dev/null 2>&1; then
    setsid "$NODE_BIN" scripts/forge-local-first-app.mjs dev-app > "$LOG_PATH" 2>&1 < /dev/null &
  else
    nohup "$NODE_BIN" scripts/forge-local-first-app.mjs dev-app > "$LOG_PATH" 2>&1 < /dev/null &
  fi
  FORGE_APP_PID=$!
  SERVER_STARTED=true
  printf '%s\n' "$FORGE_APP_PID" > "$PID_PATH"
fi
i=0
while [ "$i" -lt 60 ]; do
  DETECTED_URL="$(current_app_url || true)"
  if [ -n "$DETECTED_URL" ]; then
    APP_URL="$DETECTED_URL"
    APP_URL_SOURCE="current-app.json"
    HAS_CURRENT_APP=true
  fi
  if [ -n "$APP_URL" ] && command -v curl >/dev/null 2>&1 && curl -fsS "$APP_URL" >/dev/null 2>&1; then
    APP_READY=true
    break
  fi
  i=$((i + 1))
  sleep 1
done
if [ -z "$APP_URL_SOURCE" ]; then
  cat > "$PROOF_PATH" <<PROOF
{
  "schema": "forge.local-first-control-room.launcher-proof.v1",
  "status": "local_app_state_missing",
  "createdAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "appUrl": "$APP_URL",
  "appUrlSource": "",
  "currentAppStatePath": "$CURRENT_APP_PATH",
  "serverStarted": $SERVER_STARTED,
  "appReachable": false,
  "browserOpenAttempted": false,
  "browserOpenCommandSucceeded": false,
  "terminalOnly": false
}
PROOF
  printf 'Forge started the local app, but %s was not written. Refusing to treat a fallback port as this install.\n' "$CURRENT_APP_PATH" >&2
  exit 1
fi
OPENED=false
if [ "$APP_READY" != true ]; then
  cat > "$PROOF_PATH" <<PROOF
{
  "schema": "forge.local-first-control-room.launcher-proof.v1",
  "status": "local_app_unreachable",
  "createdAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "appUrl": "$APP_URL",
  "appUrlSource": "$APP_URL_SOURCE",
  "currentAppStatePath": "$CURRENT_APP_PATH",
  "serverStarted": $SERVER_STARTED,
  "appReachable": false,
  "browserOpenAttempted": false,
  "browserOpenCommandSucceeded": false,
  "terminalOnly": false
}
PROOF
  printf 'Forge app did not become reachable at %s. See %s for details.\n' "$APP_URL" "$LOG_PATH" >&2
  exit 1
fi
if command -v open >/dev/null 2>&1; then
  open "$APP_URL" && OPENED=true
elif command -v xdg-open >/dev/null 2>&1; then
  xdg-open "$APP_URL" >/dev/null 2>&1 && OPENED=true || true
else
  printf 'Open %s in your browser.\n' "$APP_URL"
fi
cat > "$PROOF_PATH" <<PROOF
{
  "schema": "forge.local-first-control-room.launcher-proof.v1",
  "status": "browser_open_attempted",
  "createdAt": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "appUrl": "$APP_URL",
  "appUrlSource": "$APP_URL_SOURCE",
  "currentAppStatePath": "$CURRENT_APP_PATH",
  "serverStarted": $SERVER_STARTED,
  "appReachable": true,
  "browserOpenAttempted": true,
  "browserOpenCommandSucceeded": $OPENED,
  "terminalOnly": false
}
PROOF
printf 'Forge app opened at %s\n' "$APP_URL"
printf 'Launcher proof: %s\n' "$PROOF_PATH"
EOF
  chmod +x "$LAUNCHER_PATH"

  case "$(uname -s 2>/dev/null || echo unknown)" in
    Darwin)
      if [ -d "$HOME/Desktop" ]; then
        MAC_COMMAND="$HOME/Desktop/Forge.command"
        cat > "$MAC_COMMAND" <<EOF
#!/bin/sh
exec "$LAUNCHER_PATH"
EOF
        chmod +x "$MAC_COMMAND"
        echo "Created macOS Desktop launcher: $MAC_COMMAND"
      fi
      ;;
    Linux)
      APP_DIR="$HOME/.local/share/applications"
      mkdir -p "$APP_DIR"
      DESKTOP_FILE="$APP_DIR/forge.desktop"
      cat > "$DESKTOP_FILE" <<EOF
[Desktop Entry]
Type=Application
Name=Forge
Comment=Open the Forge local app
Exec=$LAUNCHER_PATH
Icon=$ICON_PATH
Terminal=false
Categories=Utility;Development;
EOF
      chmod +x "$DESKTOP_FILE"
      echo "Created Linux app launcher: $DESKTOP_FILE"
      if [ -d "$HOME/Desktop" ]; then
        cp "$DESKTOP_FILE" "$HOME/Desktop/Forge.desktop"
        chmod +x "$HOME/Desktop/Forge.desktop"
        echo "Created Linux Desktop launcher: $HOME/Desktop/Forge.desktop"
      fi
      ;;
  esac
  echo "Launcher script: $LAUNCHER_PATH"
}

ensure_generated_artifacts_ignored() {
  if [ -d "$INSTALL_ROOT/.git" ]; then
    EXCLUDE_FILE="$INSTALL_ROOT/.git/info/exclude"
    mkdir -p "$(dirname "$EXCLUDE_FILE")"
    grep -qxF "/open-forge-app.sh" "$EXCLUDE_FILE" 2>/dev/null || printf '%s
' "/open-forge-app.sh" >> "$EXCLUDE_FILE"
    grep -qxF "/forge-icon.svg" "$EXCLUDE_FILE" 2>/dev/null || printf '%s
' "/forge-icon.svg" >> "$EXCLUDE_FILE"
  fi
}

if ! command_exists git; then
  echo "Git is required for the source/dev Forge bootstrap." >&2
  exit 1
fi

if ! command_exists node; then
  echo "Node.js was not found. Install Node.js 22 LTS, then run this script again." >&2
  exit 1
fi

NODE_MAJOR="$(node --version | sed 's/^v//' | cut -d. -f1)"
case "$NODE_MAJOR" in
  ''|*[!0-9]*)
    echo "Unable to detect Node.js version. Install Node.js 22 LTS, then try again." >&2
    exit 1
    ;;
esac

if [ "$NODE_MAJOR" -lt 22 ]; then
  echo "Forge source/dev mode expects Node.js 22 LTS or newer. Detected Node.js major version $NODE_MAJOR." >&2
  exit 1
fi

if [ "$NODE_MAJOR" -ge 24 ]; then
  echo "Warning: Node.js 24 can fail on native dependencies such as isolated-vm during full installs." >&2
  echo "The local Forge app bootstrap avoids full npm install. Use Node.js 22 LTS for full source/dev work." >&2
fi

REPO_URL="${FORGE_REPO_URL:-https://github.com/mastermers-spec/forge-v7.git}"
INSTALL_ROOT="${FORGE_INSTALL_ROOT:-$HOME/forge}"

if [ -e "$INSTALL_ROOT" ]; then
  if [ -d "$INSTALL_ROOT/.git" ]; then
    ensure_generated_artifacts_ignored
    UPDATE_FAILED=0
    if [ -n "$(git -C "$INSTALL_ROOT" status --porcelain)" ]; then
      UPDATE_FAILED=1
      echo "Existing Forge checkout has local changes, so Forge will not overwrite it." >&2
    else
      step "Update existing Forge checkout at $INSTALL_ROOT"
      git -C "$INSTALL_ROOT" fetch origin main --prune || UPDATE_FAILED=1
      if [ "$UPDATE_FAILED" -eq 0 ]; then
        git -C "$INSTALL_ROOT" switch main || git -C "$INSTALL_ROOT" switch -c main --track origin/main || UPDATE_FAILED=1
      fi
      if [ "$UPDATE_FAILED" -eq 0 ]; then
        git -C "$INSTALL_ROOT" pull --ff-only origin main || UPDATE_FAILED=1
      fi
    fi
    if [ "$UPDATE_FAILED" -ne 0 ]; then
      PREVIOUS_ROOT="$INSTALL_ROOT"
      INSTALL_ROOT="$HOME/forge-source-$(date +%Y%m%d-%H%M%S)"
      echo "Could not safely update $PREVIOUS_ROOT without risking local work or stale branch state." >&2
      echo "Creating a fresh Forge folder instead: $INSTALL_ROOT" >&2
      step "Clone Forge source/dev checkout"
      clone_repo "$INSTALL_ROOT"
      ensure_generated_artifacts_ignored
    fi
  else
    INSTALL_ROOT="$HOME/forge-source-$(date +%Y%m%d-%H%M%S)"
    echo "$HOME/forge already exists and is not a Git checkout." >&2
    echo "Creating a new Forge folder instead: $INSTALL_ROOT" >&2
    step "Clone Forge source/dev checkout"
    clone_repo "$INSTALL_ROOT"
    ensure_generated_artifacts_ignored
  fi
else
  step "Clone Forge source/dev checkout"
  clone_repo "$INSTALL_ROOT"
  ensure_generated_artifacts_ignored
fi

cd "$INSTALL_ROOT"

step "Check the local Forge app path"
npm run forge:local-first -- status

step "Run Forge doctor"
npm run forge:local-first -- doctor

step "Create Forge app launcher and local receipts"
write_install_artifacts
create_launcher
echo "Install receipt: $RECEIPT_PATH"
echo "Local activation passport: $PASSPORT_PATH"

step "Starting Forge and opening the app UI"
FORGE_LOCAL_APP_STATE_DIR="$STATE_ROOT" "$LAUNCHER_PATH"

cat <<EOF

Forge is ready.
Forge is installed at: $INSTALL_ROOT
Open Forge now or later:
  Click the Forge launcher on your Desktop or app menu if it was created. It starts Forge and opens the current local app URL through your OS default handler.
  cd "$INSTALL_ROOT"
  npm run forge:local-first -- dev-app --open
If the launcher does not open Forge, check the diagnostic current app URL in:
  $STATE_ROOT/current-app.json

Signed Windows/macOS/Linux installers remain unavailable until signing credentials and release artifacts are verified.
EOF
