#!/usr/bin/env bash
# install.sh — one-liner installer for the linkdown CLI
#
# Hosted at https://github.com/0451-software/linkdown/releases/latest/download/install.sh
# Mirrored in-repo at apps/cli/install/install.sh
#
# Usage:
#   curl -fsSL https://github.com/0451-software/linkdown/releases/latest/download/install.sh | bash
#
# Options (via env vars):
#   LINKDOWN_VERSION   Pin a specific release tag (default: latest)
#   LINKDOWN_INSTALL_DIR  Override install dir (default: auto: /usr/local/bin or ~/.local/bin)
#   LINKDOWN_NO_MODIFY_PATH  If set, skip PATH hint instructions
#   LINKDOWN_DEBUG      Print extra diagnostic output
#
# This script does NOT require sudo. It installs to ~/.local/bin by default
# (which is on PATH for most modern macOS / Linux setups) and only uses
# /usr/local/bin if it's already writable by the current user.
#
# Supported platforms: macOS (arm64, x64), Linux (x64, arm64).
#
# See docs/install.md for troubleshooting and manual alternatives.
set -euo pipefail

# --- Pretty output --------------------------------------------------------
if [[ -t 1 ]]; then
  C_RESET=$'\033[0m'
  C_BOLD=$'\033[1m'
  C_DIM=$'\033[2m'
  C_GREEN=$'\033[32m'
  C_YELLOW=$'\033[33m'
  C_RED=$'\033[31m'
  C_CYAN=$'\033[36m'
else
  C_RESET="" C_BOLD="" C_DIM="" C_GREEN="" C_YELLOW="" C_RED="" C_CYAN=""
fi

info()  { printf "%s[info]%s  %s\n" "$C_CYAN"   "$C_RESET" "$*"; }
ok()    { printf "%s[ ok]%s  %s\n" "$C_GREEN"  "$C_RESET" "$*"; }
warn()  { printf "%s[warn]%s  %s\n" "$C_YELLOW" "$C_RESET" "$*" >&2; }
err()   { printf "%s[fail]%s %s\n" "$C_RED"    "$C_RESET" "$*" >&2; }
debug() { [[ -n "${LINKDOWN_DEBUG:-}" ]] && printf "%s[dbg ]%s  %s\n" "$C_DIM" "$C_RESET" "$*" >&2 || true; }

# --- Cleanup on exit ------------------------------------------------------
TMP_DIR=""
cleanup() {
  if [[ -n "$TMP_DIR" && -d "$TMP_DIR" ]]; then
    debug "Cleaning up $TMP_DIR"
    rm -rf "$TMP_DIR"
  fi
}
trap cleanup EXIT

# --- OS / arch detection --------------------------------------------------
uname_os() {
  local os
  os="$(uname -s | tr '[:upper:]' '[:lower:]')"
  case "$os" in
    darwin) echo "macos" ;;
    linux)  echo "linux" ;;
    *)
      err "Unsupported operating system: $os"
      err "This installer supports macOS and Linux only."
      err "For Windows, see docs/install.md (manual install via cargo)."
      exit 1
      ;;
  esac
}

uname_arch() {
  local arch
  arch="$(uname -m)"
  case "$arch" in
    arm64|aarch64) echo "arm64" ;;
    x86_64|amd64)  echo "x64"   ;;
    *)
      err "Unsupported CPU architecture: $arch"
      err "This installer supports arm64 and x86_64 (x64) only."
      exit 1
      ;;
  esac
}

# --- Required tooling check ----------------------------------------------
need_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    err "Required command not found: $1"
    case "$1" in
      tar)  err "Install tar (e.g. 'brew install gnu-tar' on macOS, 'apt-get install tar' on Debian/Ubuntu)." ;;
      unzip) err "Install unzip (e.g. 'brew install unzip' on macOS, 'apt-get install unzip' on Debian/Ubuntu)." ;;
      curl) err "Install curl (e.g. 'brew install curl' on macOS, 'apt-get install curl' on Debian/Ubuntu)." ;;
    esac
    exit 1
  fi
}

# --- Pick install dir -----------------------------------------------------
resolve_install_dir() {
  if [[ -n "${LINKDOWN_INSTALL_DIR:-}" ]]; then
    # Honour the override. If the directory doesn't exist yet, create it
    # (the user is explicitly asking us to install there).
    if [[ ! -d "$LINKDOWN_INSTALL_DIR" ]]; then
      debug "Creating LINKDOWN_INSTALL_DIR=$LINKDOWN_INSTALL_DIR"
      if ! mkdir -p "$LINKDOWN_INSTALL_DIR" 2>/dev/null; then
        err "LINKDOWN_INSTALL_DIR=$LINKDOWN_INSTALL_DIR does not exist and could not be created."
        err "Check the path, permissions, or unset LINKDOWN_INSTALL_DIR to use the default."
        exit 1
      fi
    fi
    echo "$LINKDOWN_INSTALL_DIR"
    return
  fi

  # Prefer /usr/local/bin if it's already writable by us (typical on macOS
  # for the admin user; CI; Docker containers). This avoids needing sudo.
  if [[ -d /usr/local/bin && -w /usr/local/bin ]]; then
    echo "/usr/local/bin"
    return
  fi

  # Otherwise use ~/.local/bin and ensure it exists. This is on PATH by
  # default for most modern macOS and Linux distros (Homebrew prefix on
  # macOS, XDG-based home on Linux). We do NOT require sudo.
  local user_local="${HOME}/.local/bin"
  if [[ ! -d "$user_local" ]]; then
    debug "Creating $user_local"
    mkdir -p "$user_local"
  fi
  echo "$user_local"
}

# --- PATH check -----------------------------------------------------------
path_has_dir() {
  local dir="$1"
  local IFS=':'
  for p in $PATH; do
    if [[ "$p" == "$dir" ]]; then
      return 0
    fi
  done
  return 1
}

shell_rc_hint() {
  # Print a short snippet the user can paste to add ~/.local/bin to PATH.
  local rc=""
  case "${SHELL:-/bin/sh}" in
    */zsh)  rc="${ZDOTDIR:-$HOME}/.zshrc" ;;
    */bash)
      # Prefer .bashrc on Linux, .bash_profile on macOS
      if [[ "$(uname -s)" == "Darwin" ]]; then
        rc="$HOME/.bash_profile"
      else
        rc="$HOME/.bashrc"
      fi
      ;;
    */fish) rc="$HOME/.config/fish/config.fish" ;;
    *)      rc="$HOME/.profile" ;;
  esac
  echo "$rc"
}

print_path_hint() {
  local install_dir="$1"
  if [[ -z "${LINKDOWN_NO_MODIFY_PATH:-}" ]]; then
    if ! path_has_dir "$install_dir"; then
      warn "$install_dir is not on your PATH."
      warn "Add it to your shell rc, e.g.:"
      printf "       %s\n" "echo 'export PATH=\"$install_dir:\$PATH\"' >> $(shell_rc_hint)"
      warn "Then restart your shell or 'source' the file."
    fi
  fi
}

# --- GitHub release fetch -------------------------------------------------
REPO_OWNER="0451-software"
REPO_NAME="linkdown"
GITHUB_API="https://api.github.com"

# fetch <url> <dest> — uses curl with sane defaults
fetch() {
  local url="$1"
  local dest="$2"
  debug "GET $url -> $dest"
  curl --fail --silent --show-error --location \
       --connect-timeout 15 --max-time 300 \
       --retry 3 --retry-delay 2 \
       "$url" -o "$dest"
}

# Get the tag_name from the latest release JSON, using only grep/sed so we
# don't require jq on the user's machine.
fetch_latest_tag() {
  local json="$1"
  # Match the first "tag_name": "v..." in the JSON body. GitHub's API
  # response is well-formed so this is safe.
  sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
    "$json" | head -n1
}

# Get the browser_download_url for an asset name match, using only sed.
find_asset_url() {
  local json="$1"
  local asset_name="$2"
  # Pull every "browser_download_url": "..." value, then match the one
  # whose tail (after the final `/`) equals the asset name. Multi-line
  # JSON: each asset appears on a contiguous block, so this is sufficient.
  # We use sed's BRE end-of-line anchor `$` to avoid prefix collisions
  # with longer asset names (e.g. matching `linkdown` as a prefix of
  # `linkdown-macos-arm64.zip`).
  sed -n 's/.*"browser_download_url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$json" \
    | sed -n "\|/${asset_name}$|p" \
    | head -n1
}

# --- Main -----------------------------------------------------------------
main() {
  info "linkdown CLI installer"
  printf "\n"

  # 1. Pre-flight: required tools
  need_cmd curl
  if [[ "$(uname_os)" == "macos" ]]; then
    need_cmd tar
  else
    # Linux: we accept either tar or unzip depending on the asset.
    command -v tar >/dev/null 2>&1 || command -v unzip >/dev/null 2>&1 \
      || { err "Need either 'tar' or 'unzip' installed."; exit 1; }
  fi

  local os
  os="$(uname_os)"
  local arch
  arch="$(uname_arch)"
  info "Detected: ${C_BOLD}${os}-${arch}${C_RESET}"

  # 2. Resolve install dir
  local install_dir
  install_dir="$(resolve_install_dir)"
  info "Install dir: ${C_BOLD}${install_dir}${C_RESET}"

  # 3. Make a working dir
  TMP_DIR="$(mktemp -d -t linkdown-install.XXXXXX)"
  debug "Workdir: $TMP_DIR"

  # 4. Fetch release metadata
  local meta_json="$TMP_DIR/release.json"
  if [[ -n "${LINKDOWN_VERSION:-}" ]]; then
    info "Fetching release metadata for tag ${LINKDOWN_VERSION} ..."
    fetch "${GITHUB_API}/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/${LINKDOWN_VERSION}" "$meta_json"
  else
    info "Fetching latest release metadata ..."
    fetch "${GITHUB_API}/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" "$meta_json"
  fi

  local tag
  tag="$(fetch_latest_tag "$meta_json")"
  if [[ -z "$tag" ]]; then
    err "Could not determine release tag from GitHub API response."
    err "Raw response was:"
    sed 's/^/    /' "$meta_json" >&2 || true
    exit 1
  fi
  info "Latest release: ${C_BOLD}${tag}${C_RESET}"

  # 5. Pick the right asset
  # Convention: linkdown-{os}-{arch}.tar.gz on Linux, .zip on macOS
  # (matches Rust cross-compile outputs from .github/workflows/release.yml).
  local ext="tar.gz"
  if [[ "$os" == "macos" ]]; then
    ext="zip"
  fi
  local asset_name="linkdown-${os}-${arch}.${ext}"
  info "Looking for asset: ${C_BOLD}${asset_name}${C_RESET}"

  local download_url
  download_url="$(find_asset_url "$meta_json" "$asset_name")"
  if [[ -z "$download_url" ]]; then
    err "Release ${tag} has no asset named ${asset_name}."
    err "Available assets in this release:"
    sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/    \1/p' "$meta_json" | head -n 50 >&2
    err "If your platform is missing, please open an issue:"
    err "    https://github.com/${REPO_OWNER}/${REPO_NAME}/issues/new"
    exit 1
  fi

  # 6. Download
  local archive="$TMP_DIR/$asset_name"
  info "Downloading $asset_name ..."
  fetch "$download_url" "$archive"

  # 7. Extract
  info "Extracting ..."
  case "$ext" in
    tar.gz)
      tar -xzf "$archive" -C "$TMP_DIR"
      ;;
    zip)
      unzip -q -o "$archive" -d "$TMP_DIR"
      ;;
    *)
      err "Internal: unknown archive extension $ext"
      exit 1
      ;;
  esac

  # 8. Find the binary inside the archive. Convention: archives are flat
  # (no top-level dir), with the binary named 'linkdown'. Fall back to
  # scanning if the convention is broken.
  local bin_path="$TMP_DIR/linkdown"
  if [[ ! -x "$bin_path" ]]; then
    # Search for any executable named linkdown within the extracted tree.
    bin_path="$(find "$TMP_DIR" -maxdepth 4 -type f -name 'linkdown' -perm -u+x | head -n1 || true)"
  fi
  if [[ -z "$bin_path" || ! -x "$bin_path" ]]; then
    err "Could not find an executable named 'linkdown' in the archive."
    err "Archive contents:"
    ( cd "$TMP_DIR" && find . -maxdepth 3 -print ) >&2
    exit 1
  fi

  # 9. Install
  info "Installing to $install_dir/linkdown ..."
  # Remove any existing install so we get a clean copy (handles both the
  # "we're overwriting an older version" and "different user owns it" cases).
  if [[ -e "$install_dir/linkdown" ]]; then
    if [[ ! -w "$install_dir/linkdown" ]]; then
      err "Existing $install_dir/linkdown is not writable by the current user."
      err "Remove it manually (or re-run as the owning user) and try again."
      exit 1
    fi
    rm -f "$install_dir/linkdown"
  fi
  install -m 0755 "$bin_path" "$install_dir/linkdown"

  # 10. Verify
  if "$install_dir/linkdown" --version >/dev/null 2>&1; then
    local installed_version
    installed_version="$("$install_dir/linkdown" --version 2>&1 | head -n1)"
    ok "Installed: $installed_version"
  else
    warn "Installed $install_dir/linkdown but 'linkdown --version' failed."
    warn "The binary may be corrupted or the wrong architecture."
  fi

  # 11. PATH hint
  if ! path_has_dir "$install_dir"; then
    print_path_hint "$install_dir"
  fi

  # 12. Next steps
  printf "\n"
  printf "%sNext step:%s  run %s\n" "$C_BOLD" "$C_RESET" "$C_GREEN> linkdown login$C_RESET to authenticate."
  printf "\n"
  printf "%sOther useful commands:%s\n" "$C_DIM" "$C_RESET"
  printf "    linkdown --help     # list all commands\n"
  printf "    linkdown whoami     # show the authenticated account\n"
  printf "    linkdown create     # upload a deck and create a share link\n"
  printf "\n"
  printf "Docs:     %s\n" "https://github.com/${REPO_OWNER}/${REPO_NAME}/blob/main/apps/cli/README.md"
  printf "Releases: %s\n" "https://github.com/${REPO_OWNER}/${REPO_NAME}/releases"
}

main "$@"
