#!/bin/sh
# Flieber CLI installer for macOS and Linux (AIDEV-801).
#
#   curl -fsSL https://build.flieber.com/install.sh | sh -s -- <org_id>
#   curl -fsSL https://build.flieberqa.com/install.sh | sh -s -- <org_id> --qa
#
# Installs uv if missing (no sudo, no Python needed), installs or upgrades flieber, turns on
# onboarding writes, adds the flieber skills for Claude Code when it is there, signs in through the
# browser and checks the sign-in. Safe to run again: running it again is the fix. POSIX sh only (tests
# run it under dash). The Windows twins are flieber-install.bat and install.ps1;
# tests/test_install_scripts.py covers all three.
#
# FLIEBER_INSTALL_SKIP_LOGIN=1 stops after the install (CI).

set -eu

UV_INSTALLER_URL="https://astral.sh/uv/install.sh"
WRITES_LINE="export FLIEBER_OAUTH_WRITES=1"

say() {
  printf '%s\n' "$*"
}

fail() {
  printf '\n%s\n' "$*" >&2
  printf '%s\n' "Setup stopped. Nothing is broken: fix the message above and run the same command again." >&2
  exit 1
}

find_uv() {
  if command -v uv >/dev/null 2>&1; then
    command -v uv
    return 0
  fi
  for candidate in "${XDG_BIN_HOME:-$HOME/.local/bin}/uv" "$HOME/.local/bin/uv" "$HOME/.cargo/bin/uv"; do
    if [ -x "$candidate" ]; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done
  return 1
}

# The shell settings files that new terminals of this user read.
profile_files() {
  case "${SHELL:-}" in
    */zsh) printf '%s\n' "$HOME/.zshrc" ;;
    */bash)
      printf '%s\n' "$HOME/.bashrc"
      if [ "$(uname -s)" = "Darwin" ]; then
        printf '%s\n' "$HOME/.bash_profile"
      fi
      ;;
    *) printf '%s\n' "$HOME/.profile" ;;
  esac
}

# Onboarding writes stay on in new terminals. Added once; running the installer again changes nothing.
persist_writes() {
  # One path per line, read whole: a home folder with a space ("John Smith") stays one path.
  profile_files | while IFS= read -r file; do
    if [ ! -f "$file" ] || ! grep -qxF "$WRITES_LINE" "$file"; then
      printf '\n# Flieber: let flieber make onboarding changes (always previewed first)\n%s\n' \
        "$WRITES_LINE" >>"$file"
    fi
  done
}

# The agent skills (AIDEV-833), when Claude Code is on this computer (its config folder exists): they need
# no sign-in, and a failure (an older flieber has no `skills` command) never stops the setup.
add_skills() {
  claude_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
  [ -d "$claude_dir" ] || return 0
  if "$flieber" skills install --client claude-code --scope user >/dev/null 2>&1; then
    say "Added the flieber skills for Claude Code."
  else
    say "Skipped the flieber skills for Claude Code; \`flieber skills install\` says why. flieber itself is ready."
  fi
}

# flieber, pointed at QA when --qa was given.
run_flieber() {
  if [ -n "$qa" ]; then
    "$flieber" --env qa "$@"
  else
    "$flieber" "$@"
  fi
}

# A new sign-up has no organisation yet (AIDEV-830): `flieber signup` creates one, after the person reads and
# accepts Flieber's terms. Its prompts need a terminal, and `curl ... | sh` gives the script itself on stdin, so it
# runs on /dev/tty, and only when one can be opened. A failure, a "no" or no terminal never stops the setup.
# FLIEBER_INSTALL_TTY names another terminal (tests give a file). Succeeds only when it created one.
offer_signup() {
  tty="${FLIEBER_INSTALL_TTY:-/dev/tty}"
  if [ -n "$qa" ]; then
    command_line="flieber --env qa signup"
  else
    command_line="flieber signup"
  fi
  say "No Flieber organisation yet? Create one with: $command_line"
  # Only `no_organization` means "none yet". A member of several is signed in with none selected (ADR-0005
  # D4): they choose one of theirs, they don't need another. Read by its code, which `--format json` writes
  # to stdout: the exit code alone (3) is shared with a missing or refused sign-in.
  answer=""
  if (: <"$tty") 2>/dev/null; then
    answer=$(run_flieber --format json ping 2>/dev/null) || :
  fi
  case "$answer" in
    *'"code": "no_organization"'*) ;;
    *) return 1 ;;
  esac
  say ""
  if run_flieber signup <"$tty"; then
    return 0
  fi
  say "No organisation was created. Run $command_line when you're ready."
  say ""
  return 1
}

main() {
  org=""
  qa=""
  for arg in "$@"; do
    case "$arg" in
      --qa) qa=1 ;;
      -*) fail "Unknown option $arg. Usage: sh install.sh <org_id> [--qa]" ;;
      *) org="$arg" ;;
    esac
  done

  say ""
  say "=== Flieber setup ==="
  say ""

  uv="$(find_uv)" || uv=""
  if [ -z "$uv" ]; then
    say "[1/4] Installing uv, the tool that installs flieber..."
    curl -LsSf "$UV_INSTALLER_URL" | sh || true
    uv="$(find_uv)" || fail "Could not install uv. Your network may block the download.
Ask your IT team to allow astral.sh, releases.astral.sh, github.com, pypi.org and files.pythonhosted.org,
or install uv by hand: https://docs.astral.sh/uv/"
  fi
  say "[1/4] uv is ready."

  say "[2/4] Installing flieber..."
  "$uv" tool install --upgrade flieber || fail "Could not install flieber. Check your internet connection
(IT may need to allow astral.sh, releases.astral.sh, github.com, pypi.org and files.pythonhosted.org)."
  "$uv" tool update-shell >/dev/null 2>&1 || true
  bin_dir="$("$uv" tool dir --bin)" || fail "Your uv is too old to install flieber. Update it: $uv self update"
  flieber="$bin_dir/flieber"
  [ -x "$flieber" ] || fail "flieber was installed but not found at $flieber. Open a new terminal window."

  FLIEBER_OAUTH_WRITES=1
  export FLIEBER_OAUTH_WRITES
  persist_writes
  add_skills

  # QA (Flieber staff) is for this run only: persisting FLIEBER_ENV would point every later run at QA.
  if [ -n "$qa" ]; then
    FLIEBER_PROFILE=qa
    export FLIEBER_PROFILE
  fi

  if [ "${FLIEBER_INSTALL_SKIP_LOGIN:-}" = "1" ]; then
    run_flieber --version
    return 0
  fi

  say "[3/4] Opening the Flieber sign-in in your browser. Finish it there, then come back here."
  if [ -z "$org" ]; then
    run_flieber login || fail "The sign-in did not finish."
    say ""
    say "Your organisations:"
    run_flieber tenants || true
    say ""
    if offer_signup; then
      say ""
      say "Done. flieber is installed and signed in to your new organisation."
      say "Open a NEW terminal window to use the flieber command yourself."
      return 0
    fi
    say "If you have an organisation, run this again with its id at the end (from the Developer page of the Flieber portal)."
    return 0
  fi
  run_flieber login --tenant "$org" || fail "The sign-in did not finish."

  say "[4/4] Checking the connection..."
  run_flieber ping || fail "Signed in, but the check failed. The message above says why."
  say ""
  run_flieber onboard status || true
  say ""
  say "Done. flieber is installed and signed in."
  say "Open a NEW terminal window to use the flieber command yourself."
}

main "$@"
