#!/bin/sh
# Glitch installer — fetches a platform-specific binary, verifies it,
# installs to ~/.glitch/bin/, and (optionally) wires up PATH.
#
# Usage:
#   curl -fsSL https://install.heyglitch.dev | sh
#   curl -fsSL https://install.heyglitch.dev | sh -s -- 1.2.3
#   curl -fsSL https://install.heyglitch.dev | sh -s -- latest
#
# Environment overrides:
#   DOWNLOAD_BASE_URL      base URL for releases (default: dl.heyglitch.dev)
#   GLITCH_INSTALL_DIR     install dir (default: ~/.glitch/bin)
#   GLITCH_NO_MODIFY_PATH  set to 1 to skip PATH editing

set -e

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------

DOWNLOAD_BASE_URL="${DOWNLOAD_BASE_URL:-https://dl.heyglitch.dev/glitch-releases}"
INSTALL_DIR="${GLITCH_INSTALL_DIR:-$HOME/.glitch/bin}"
VERSION_ARG="${1:-latest}"

# ---------------------------------------------------------------------------
# Colors (disabled if not a terminal)
# ---------------------------------------------------------------------------

if [ -t 1 ]; then
  RED='\033[0;31m'
  GREEN='\033[0;32m'
  YELLOW='\033[0;33m'
  BLUE='\033[0;34m'
  BOLD='\033[1m'
  RESET='\033[0m'
else
  RED=''
  GREEN=''
  YELLOW=''
  BLUE=''
  BOLD=''
  RESET=''
fi

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

info() {
  printf "${BLUE}info${RESET}  %s\n" "$1"
}

success() {
  printf "${GREEN}ok${RESET}    %s\n" "$1"
}

warn() {
  printf "${YELLOW}warn${RESET}  %s\n" "$1" >&2
}

fail() {
  printf "${RED}error${RESET} %s\n" "$1" >&2
  exit 1
}

# Detect a download command (prefer curl, fall back to wget)
detect_downloader() {
  if command -v curl >/dev/null 2>&1; then
    DOWNLOADER="curl"
  elif command -v wget >/dev/null 2>&1; then
    DOWNLOADER="wget"
  else
    fail "Neither curl nor wget found. Please install one and retry."
  fi
}

# Download a URL to a file. Usage: download <url> <output_file>
download() {
  url="$1"
  output="$2"

  if [ "$DOWNLOADER" = "curl" ]; then
    curl -fsSL --retry 3 --retry-delay 2 -o "$output" "$url"
  else
    wget -q --tries=3 -O "$output" "$url"
  fi
}

# Download a URL to stdout. Usage: download_stdout <url>
download_stdout() {
  url="$1"

  if [ "$DOWNLOADER" = "curl" ]; then
    curl -fsSL --retry 3 --retry-delay 2 "$url"
  else
    wget -q --tries=3 -O - "$url"
  fi
}

# ---------------------------------------------------------------------------
# Platform detection
# ---------------------------------------------------------------------------

detect_platform() {
  OS="$(uname -s)"
  ARCH="$(uname -m)"

  case "$OS" in
    Darwin)  PLATFORM_OS="darwin" ;;
    Linux)   PLATFORM_OS="linux" ;;
    MINGW*|MSYS*|CYGWIN*|Windows_NT)
      fail "Windows is not yet supported for native binary installation.
  Please use npm: npm install -g @glitch/cli
  PowerShell installer coming soon (LAUNCH-P0-DIST-04)."
      ;;
    *)
      fail "Unsupported operating system: $OS"
      ;;
  esac

  case "$ARCH" in
    arm64|aarch64)  PLATFORM_ARCH="arm64" ;;
    x86_64|amd64)   PLATFORM_ARCH="x64" ;;
    *)
      fail "Unsupported architecture: $ARCH. Glitch supports arm64 and x86_64."
      ;;
  esac

  TARGET="${PLATFORM_OS}-${PLATFORM_ARCH}"
  BINARY_NAME="glitch-${TARGET}"
  TARBALL_NAME="${BINARY_NAME}.tar.gz"
}

# ---------------------------------------------------------------------------
# Version resolution
# ---------------------------------------------------------------------------

resolve_version() {
  case "$VERSION_ARG" in
    latest|stable)
      info "Resolving latest version..."
      MANIFEST_URL="${DOWNLOAD_BASE_URL}/latest/manifest.json"
      ;;
    *)
      # Treat as a specific version number
      # Strip leading 'v' if present
      VERSION_ARG="$(echo "$VERSION_ARG" | sed 's/^v//')"
      info "Resolving version ${VERSION_ARG}..."
      MANIFEST_URL="${DOWNLOAD_BASE_URL}/${VERSION_ARG}/manifest.json"
      ;;
  esac

  # Download manifest to a temp file
  TMPDIR_INSTALL="$(mktemp -d)"
  trap 'rm -rf "$TMPDIR_INSTALL"' EXIT

  MANIFEST_FILE="${TMPDIR_INSTALL}/manifest.json"

  if ! download "$MANIFEST_URL" "$MANIFEST_FILE" 2>/dev/null; then
    fail "Could not download release manifest from:
  ${MANIFEST_URL}

  If you specified a version, check that it exists.
  Available versions: ${DOWNLOAD_BASE_URL}"
  fi

  # Parse JSON manifest portably (no jq dependency).
  # Normalize: put each "key":"value" pair on its own line, stripping
  # braces/brackets/commas. Works for both compact and pretty-printed JSON.
  MANIFEST_LINES="${TMPDIR_INSTALL}/manifest.lines"
  tr '{' '\n' < "$MANIFEST_FILE" | tr '}' '\n' | tr ',' '\n' | \
    sed 's/^ *//g; s/ *$//g; /^$/d' > "$MANIFEST_LINES"

  # Parse version
  VERSION="$(grep '"version"' "$MANIFEST_LINES" | \
    sed 's/.*"version" *: *"\([^"]*\)".*/\1/' | head -1)"
  if [ -z "$VERSION" ]; then
    fail "Could not parse version from manifest."
  fi

  # Extract checksums for our target platform.
  # The normalized manifest has lines like:
  #   "darwin-arm64":
  #   "sha256":"abc123..."
  #   "tarball_sha256":"def456..."
  # We find the target line, then read following lines until the next
  # platform block or EOF.
  EXPECTED_BINARY_SHA256=""
  EXPECTED_TARBALL_SHA256=""

  # Use awk to extract the section after our target key
  TARGET_SECTION="$(awk "
    /\"${TARGET}\"/ { found=1; next }
    found && /\"[a-z]+-[a-z0-9]+\"/ { exit }
    found { print }
  " "$MANIFEST_LINES")"

  if [ -n "$TARGET_SECTION" ]; then
    EXPECTED_BINARY_SHA256="$(echo "$TARGET_SECTION" | \
      grep '"sha256"' | grep -v 'tarball_sha256' | \
      sed 's/.*"sha256" *: *"\([a-f0-9]*\)".*/\1/' | head -1)"

    EXPECTED_TARBALL_SHA256="$(echo "$TARGET_SECTION" | \
      grep '"tarball_sha256"' | \
      sed 's/.*"tarball_sha256" *: *"\([a-f0-9]*\)".*/\1/' | head -1)"
  fi

  if [ -z "$EXPECTED_BINARY_SHA256" ]; then
    fail "No binary found for your platform ($TARGET) in version $VERSION.
  Check that the release includes a binary for ${TARGET}."
  fi

  success "Found Glitch v${VERSION} for ${TARGET}"
}

# ---------------------------------------------------------------------------
# Download + verify
# ---------------------------------------------------------------------------

download_and_verify() {
  # Determine download URL
  TARBALL_URL="${DOWNLOAD_BASE_URL}/${VERSION}/${TARBALL_NAME}"

  info "Downloading ${TARBALL_NAME}..."
  TARBALL_FILE="${TMPDIR_INSTALL}/${TARBALL_NAME}"
  if ! download "$TARBALL_URL" "$TARBALL_FILE"; then
    fail "Download failed: ${TARBALL_URL}"
  fi

  # Verify tarball checksum (if available in manifest)
  if [ -n "$EXPECTED_TARBALL_SHA256" ]; then
    info "Verifying tarball checksum..."
    if command -v shasum >/dev/null 2>&1; then
      ACTUAL_SHA256="$(shasum -a 256 "$TARBALL_FILE" | awk '{ print $1 }')"
    elif command -v sha256sum >/dev/null 2>&1; then
      ACTUAL_SHA256="$(sha256sum "$TARBALL_FILE" | awk '{ print $1 }')"
    else
      warn "Neither shasum nor sha256sum found; skipping checksum verification."
      ACTUAL_SHA256="$EXPECTED_TARBALL_SHA256"
    fi

    if [ "$ACTUAL_SHA256" != "$EXPECTED_TARBALL_SHA256" ]; then
      fail "Checksum mismatch for ${TARBALL_NAME}!
  Expected: ${EXPECTED_TARBALL_SHA256}
  Got:      ${ACTUAL_SHA256}

  The download may be corrupted or tampered with. Try again or report this issue."
    fi
    success "Checksum verified"
  else
    # Fall back to binary checksum after extraction
    info "Tarball checksum not in manifest; will verify binary checksum after extraction."
  fi

  # Extract
  info "Extracting..."
  tar xzf "$TARBALL_FILE" -C "$TMPDIR_INSTALL"

  EXTRACTED_BINARY="${TMPDIR_INSTALL}/${BINARY_NAME}"
  if [ ! -f "$EXTRACTED_BINARY" ]; then
    fail "Expected binary not found after extraction: ${BINARY_NAME}"
  fi

  # Verify binary checksum
  info "Verifying binary checksum..."
  if command -v shasum >/dev/null 2>&1; then
    ACTUAL_BIN_SHA256="$(shasum -a 256 "$EXTRACTED_BINARY" | awk '{ print $1 }')"
  elif command -v sha256sum >/dev/null 2>&1; then
    ACTUAL_BIN_SHA256="$(sha256sum "$EXTRACTED_BINARY" | awk '{ print $1 }')"
  else
    warn "Cannot verify binary checksum (no shasum/sha256sum)."
    ACTUAL_BIN_SHA256="$EXPECTED_BINARY_SHA256"
  fi

  if [ "$ACTUAL_BIN_SHA256" != "$EXPECTED_BINARY_SHA256" ]; then
    fail "Binary checksum mismatch!
  Expected: ${EXPECTED_BINARY_SHA256}
  Got:      ${ACTUAL_BIN_SHA256}

  The download may be corrupted or tampered with."
  fi
  success "Binary checksum verified"
}

# ---------------------------------------------------------------------------
# Install
# ---------------------------------------------------------------------------

install_binary() {
  info "Installing to ${INSTALL_DIR}..."

  # Create install directory
  mkdir -p "$INSTALL_DIR"

  # Install versioned binary
  VERSIONED_NAME="glitch-${VERSION}"
  cp "$EXTRACTED_BINARY" "${INSTALL_DIR}/${VERSIONED_NAME}"
  chmod +x "${INSTALL_DIR}/${VERSIONED_NAME}"

  # Create/update symlink
  ln -sf "${VERSIONED_NAME}" "${INSTALL_DIR}/glitch"

  success "Installed glitch v${VERSION} to ${INSTALL_DIR}/glitch"
}

# ---------------------------------------------------------------------------
# PATH setup
# ---------------------------------------------------------------------------

setup_path() {
  # Check if already in PATH
  case ":$PATH:" in
    *":${INSTALL_DIR}:"*)
      success "~/.glitch/bin is already in your PATH"
      return
      ;;
  esac

  if [ "${GLITCH_NO_MODIFY_PATH:-0}" = "1" ]; then
    warn "~/.glitch/bin is not in your PATH. Add it manually:"
    warn "  export PATH=\"${INSTALL_DIR}:\$PATH\""
    return
  fi

  info ""
  info "~/.glitch/bin is not in your PATH."
  info ""

  # Detect shell and config file
  SHELL_NAME="$(basename "${SHELL:-/bin/sh}")"
  case "$SHELL_NAME" in
    zsh)   SHELL_RC="$HOME/.zshrc" ;;
    bash)
      # Prefer .bashrc on Linux, .bash_profile on macOS
      if [ -f "$HOME/.bash_profile" ]; then
        SHELL_RC="$HOME/.bash_profile"
      else
        SHELL_RC="$HOME/.bashrc"
      fi
      ;;
    fish)  SHELL_RC="$HOME/.config/fish/config.fish" ;;
    *)     SHELL_RC="$HOME/.profile" ;;
  esac

  PATH_LINE="export PATH=\"\$HOME/.glitch/bin:\$PATH\""
  FISH_PATH_LINE="set -gx PATH \$HOME/.glitch/bin \$PATH"

  # Check if the line is already in the rc file
  if [ -f "$SHELL_RC" ]; then
    if grep -qF '.glitch/bin' "$SHELL_RC" 2>/dev/null; then
      success "PATH entry already exists in ${SHELL_RC}"
      return
    fi
  fi

  # If running non-interactively (piped), auto-add
  if [ ! -t 0 ]; then
    if [ "$SHELL_NAME" = "fish" ]; then
      echo "" >> "$SHELL_RC"
      echo "# Glitch CLI" >> "$SHELL_RC"
      echo "$FISH_PATH_LINE" >> "$SHELL_RC"
    else
      echo "" >> "$SHELL_RC"
      echo "# Glitch CLI" >> "$SHELL_RC"
      echo "$PATH_LINE" >> "$SHELL_RC"
    fi
    success "Added ~/.glitch/bin to PATH in ${SHELL_RC}"
    info "Run 'source ${SHELL_RC}' or open a new terminal to use glitch."
    return
  fi

  # Interactive prompt
  printf "${BOLD}Add ~/.glitch/bin to PATH in ${SHELL_RC}? [Y/n]${RESET} "
  read -r REPLY
  case "$REPLY" in
    [nN]*)
      info "Skipped. Add manually:"
      if [ "$SHELL_NAME" = "fish" ]; then
        info "  $FISH_PATH_LINE"
      else
        info "  $PATH_LINE"
      fi
      ;;
    *)
      if [ "$SHELL_NAME" = "fish" ]; then
        echo "" >> "$SHELL_RC"
        echo "# Glitch CLI" >> "$SHELL_RC"
        echo "$FISH_PATH_LINE" >> "$SHELL_RC"
      else
        echo "" >> "$SHELL_RC"
        echo "# Glitch CLI" >> "$SHELL_RC"
        echo "$PATH_LINE" >> "$SHELL_RC"
      fi
      success "Added to ${SHELL_RC}"
      info "Run 'source ${SHELL_RC}' or open a new terminal to use glitch."
      ;;
  esac
}

# ---------------------------------------------------------------------------
# Uninstall script generation
# ---------------------------------------------------------------------------

create_uninstall_script() {
  UNINSTALL_SCRIPT="$HOME/.glitch/uninstall.sh"
  mkdir -p "$HOME/.glitch"
  cat > "$UNINSTALL_SCRIPT" << 'UNINSTALL_EOF'
#!/bin/sh
# uninstall.sh — Remove Glitch native binary installation
#
# Usage: ~/.glitch/uninstall.sh
#
# This removes:
#   - ~/.glitch/bin/ (all binary versions + symlink)
#   - This uninstall script
#
# It does NOT remove:
#   - ~/.glitch/pipelines/ (your pipeline definitions)
#   - ~/.glitch/runs/ (your run history)
#   - ~/.glitch/logs/ (your logs)
#   - ~/.glitch/pipeline-history/ (your artifacts)
#   - PATH entries in shell rc files (remove manually)

set -e

INSTALL_DIR="$HOME/.glitch/bin"

if [ ! -d "$INSTALL_DIR" ]; then
  echo "Glitch binary installation not found at $INSTALL_DIR"
  exit 0
fi

echo "This will remove all Glitch binary installations from:"
echo "  $INSTALL_DIR"
echo ""
echo "Your pipelines, runs, and logs in ~/.glitch/ will NOT be removed."
echo ""

if [ -t 0 ]; then
  printf "Continue? [y/N] "
  read -r REPLY
  case "$REPLY" in
    [yY]*) ;;
    *) echo "Aborted."; exit 0 ;;
  esac
fi

rm -rf "$INSTALL_DIR"
echo "Removed $INSTALL_DIR"

# Remove this script
rm -f "$HOME/.glitch/uninstall.sh"
echo "Removed uninstall script"

echo ""
echo "Glitch binary uninstalled."
echo ""
echo "Note: You may want to remove the PATH entry from your shell rc file:"
echo "  grep -n '.glitch/bin' ~/.zshrc ~/.bashrc ~/.bash_profile ~/.profile 2>/dev/null"
UNINSTALL_EOF
  chmod +x "$UNINSTALL_SCRIPT"
}

# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

main() {
  printf "\n"
  printf "${BOLD}  Glitch Installer${RESET}\n"
  printf "\n"

  detect_downloader
  detect_platform
  resolve_version
  download_and_verify
  install_binary
  create_uninstall_script
  setup_path

  printf "\n"
  printf "  ${GREEN}${BOLD}Glitch v${VERSION} installed successfully!${RESET}\n"
  printf "\n"
  printf "  Run ${BOLD}glitch --version${RESET} to verify.\n"
  printf "  Run ${BOLD}glitch init${RESET} in a project to get started.\n"
  printf "\n"
  printf "  Uninstall: ${BOLD}~/.glitch/uninstall.sh${RESET}\n"
  printf "\n"
}

main
