#!/bin/sh

set -eu
LC_ALL=C
export LC_ALL

user_path=${PATH:-}
PATH="$HOME/.local/bin:$user_path:/opt/homebrew/bin:/usr/local/bin"
export PATH

download_base_url=${OBLIVE_DOWNLOAD_BASE_URL:-https://downloads.oblive.dev}
download_base_url=${download_base_url%/}
asset_name=oblive-deployment.tar.gz
channel=latest
requested_version=""
install_dir=${OBLIVE_HOME:-"$HOME/.oblive"}
auth_mode=local
start_after_install=true
path_choice=ask
install_prerequisites=false
assume_yes=false

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

fail() {
  printf 'Oblive installer: %s\n' "$*" >&2
  exit 1
}

case "$download_base_url" in https://*) ;; *) fail "the download base URL must use HTTPS" ;; esac

usage() {
  say "Usage: install.sh [--channel latest|--version vX.Y.Z] [--install-dir path]"
  say "                  [--auth local|api-key] [--no-start]"
  say "                  [--add-to-path|--no-add-to-path]"
  say "                  [--install-prerequisites] [--yes]"
}

normalize_version() {
  normalized=$1
  case "$normalized" in v*) ;; *) normalized="v$normalized" ;; esac
  printf '%s\n' "$normalized" | awk '
    !/^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/ { exit 1 }
  ' || fail "expected a stable semantic version such as v1.2.3"
  printf '%s\n' "$normalized"
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --channel)
      [ "${2:-}" = latest ] || fail "--channel currently supports only latest"
      channel=latest
      requested_version=""
      shift 2
      ;;
    --version)
      [ -n "${2:-}" ] || fail "--version requires a value"
      requested_version=$(normalize_version "$2")
      channel=pinned
      shift 2
      ;;
    --install-dir)
      [ -n "${2:-}" ] || fail "--install-dir requires a path"
      install_dir=$2
      shift 2
      ;;
    --auth)
      case "${2:-}" in local | api-key) auth_mode=$2 ;; *) fail "--auth must be local or api-key" ;; esac
      shift 2
      ;;
    --no-start) start_after_install=false; shift ;;
    --add-to-path) path_choice=yes; shift ;;
    --no-add-to-path) path_choice=no; shift ;;
    --install-prerequisites) install_prerequisites=true; shift ;;
    --yes) assume_yes=true; shift ;;
    -h | --help) usage; exit 0 ;;
    *) fail "unknown option: $1" ;;
  esac
done

case "$install_dir" in
  "~") install_dir=$HOME ;;
  \~/*) install_dir="$HOME/${install_dir#\~/}" ;;
esac
case "$install_dir" in
  "" | / | "$HOME" | *:*) fail "the installation directory is unsafe: $install_dir" ;;
esac
case "$install_dir" in /*) ;; *) install_dir="$(pwd)/$install_dir" ;; esac

confirm() {
  prompt=$1
  default_answer=$2
  if [ "$assume_yes" = true ]; then
    return 0
  fi
  [ -r /dev/tty ] || return 1
  if [ "$default_answer" = yes ]; then
    printf '%s [Y/n] ' "$prompt" >/dev/tty
  else
    printf '%s [y/N] ' "$prompt" >/dev/tty
  fi
  IFS= read -r answer </dev/tty || return 1
  case "$answer" in
    y | Y | yes | YES | Yes) return 0 ;;
    n | N | no | NO | No) return 1 ;;
    "") [ "$default_answer" = yes ] ;;
    *) return 1 ;;
  esac
}

ask() {
  confirm "$1" no
}

os=$(uname -s)
architecture=$(uname -m)
case "$os" in Darwin | Linux) ;; *) fail "supported systems are macOS and Linux" ;; esac
case "$architecture" in x86_64 | amd64 | arm64 | aarch64) ;; *) fail "unsupported architecture: $architecture" ;; esac

for required_command in curl tar awk sed mktemp; do
  command -v "$required_command" >/dev/null 2>&1 || fail "$required_command is required"
done

temporary_root=$(mktemp -d)
trap 'rm -rf "$temporary_root"' EXIT HUP INT TERM

install_homebrew() {
  say "Homebrew is not installed. Its official installer will explain every system change."
  if [ "$install_prerequisites" = true ] || ask "Install Homebrew?"; then
    curl --fail --location --silent --show-error \
      https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh \
      --output "$temporary_root/install-homebrew.sh"
    if [ "$assume_yes" = true ]; then
      NONINTERACTIVE=1 /bin/bash "$temporary_root/install-homebrew.sh"
    else
      /bin/bash "$temporary_root/install-homebrew.sh"
    fi
    if [ -x /opt/homebrew/bin/brew ]; then eval_brew=/opt/homebrew/bin/brew
    elif [ -x /usr/local/bin/brew ]; then eval_brew=/usr/local/bin/brew
    else fail "Homebrew installation finished, but brew was not found"; fi
  else
    fail "Docker installation on macOS requires Homebrew or a manual Docker installation"
  fi
}

install_docker() {
  if [ "$os" = Darwin ]; then
    if command -v brew >/dev/null 2>&1; then eval_brew=$(command -v brew); else install_homebrew; fi
    say "Docker Desktop requires accepting Docker's terms and starting its application."
    if [ "$install_prerequisites" = true ] || ask "Install Docker Desktop with Homebrew?"; then
      "$eval_brew" install --cask docker-desktop
      open -a Docker || true
    else
      fail "Docker is required"
    fi
  else
    say "Docker's official convenience script may install packages and use elevated privileges."
    if [ "$install_prerequisites" = true ] || ask "Inspect Docker's official installation script?"; then
      curl --fail --location --silent --show-error https://get.docker.com \
        --output "$temporary_root/install-docker.sh"
      sh "$temporary_root/install-docker.sh" --dry-run
      if [ "$assume_yes" = true ] || ask "Run the Docker installation shown above?"; then
        sh "$temporary_root/install-docker.sh"
      else
        fail "Docker installation was cancelled"
      fi
    else
      fail "Docker is required"
    fi
  fi
}

if ! command -v docker >/dev/null 2>&1; then
  if [ "$install_prerequisites" = true ] || ask "Docker was not found. Install it now?"; then
    install_docker
  else
    fail "Docker is required; install it and rerun this command"
  fi
fi

if ! docker info >/dev/null 2>&1; then
  if [ "$os" = Darwin ] && command -v open >/dev/null 2>&1; then
    say "Starting Docker Desktop…"
    open -a Docker || true
    attempts=0
    while ! docker info >/dev/null 2>&1 && [ "$attempts" -lt 30 ]; do
      sleep 2
      attempts=$((attempts + 1))
    done
  fi
fi
docker info >/dev/null 2>&1 || fail "Docker is installed but its daemon is not running"
docker compose version >/dev/null 2>&1 || fail "the Docker Compose plugin is required"

if [ "$auth_mode" = local ]; then
  if ! command -v codex >/dev/null 2>&1; then
    say "Oblive local authentication uses the official Codex CLI."
    if [ "$install_prerequisites" = true ] || ask "Install Codex?"; then
      curl --fail --location --silent --show-error https://chatgpt.com/codex/install.sh \
        --output "$temporary_root/install-codex.sh"
      CODEX_NON_INTERACTIVE=1 sh "$temporary_root/install-codex.sh"
      command -v codex >/dev/null 2>&1 || fail "Codex installed, but it is not available on PATH"
    else
      fail "Codex is required for local authentication; use --auth api-key for advanced setup"
    fi
  fi
  if ! codex login status >/dev/null 2>&1; then
    [ -r /dev/tty ] || fail "Codex is not logged in; run 'codex login' and retry"
    say "Opening Codex login…"
    codex login </dev/tty >/dev/tty
    codex login status >/dev/null 2>&1 || fail "Codex login did not complete"
  fi
else
  [ -n "${OPENAI_API_KEY:-}" ] || fail "OPENAI_API_KEY is required with --auth api-key"
fi

config_home=${XDG_CONFIG_HOME:-"$HOME/.config"}/oblive
install_pointer="$config_home/install-root"
bin_dir=${OBLIVE_BIN_DIR:-"$HOME/.local/bin"}
cli_link="$bin_dir/oblive"

path_contains() {
  case ":$1:" in *":$2:"*) return 0 ;; *) return 1 ;; esac
}

resolve_shell_profile() {
  escaped_bin_dir=$(printf '%s\n' "$bin_dir" | sed 's/[\$`"\\]/\\&/g')
  case "${SHELL:-}" in
    */fish)
      shell_profile="$HOME/.config/fish/config.fish"
      path_line="fish_add_path \"$escaped_bin_dir\""
      ;;
    */zsh)
      shell_profile="$HOME/.zshrc"
      path_line="export PATH=\"$escaped_bin_dir:\$PATH\""
      ;;
    */bash)
      if [ "$os" = Darwin ]; then shell_profile="$HOME/.bash_profile"
      else shell_profile="$HOME/.bashrc"; fi
      path_line="export PATH=\"$escaped_bin_dir:\$PATH\""
      ;;
    *)
      shell_profile="$HOME/.profile"
      path_line="export PATH=\"$escaped_bin_dir:\$PATH\""
      ;;
  esac
}

configure_path() {
  path_status=active
  path_contains "$user_path" "$bin_dir" && return

  resolve_shell_profile
  if [ -f "$shell_profile" ] && grep -Fqx "$path_line" "$shell_profile"; then
    path_status=configured
    return
  fi

  if [ "$path_choice" = ask ]; then
    if confirm "Add Oblive to your PATH?" yes; then path_choice=yes
    else path_choice=no; fi
  fi
  if [ "$path_choice" = no ]; then
    path_status=skipped
    return
  fi

  mkdir -p "$(dirname "$shell_profile")"
  printf '\n# Oblive CLI\n%s\n' "$path_line" >> "$shell_profile"
  path_status=updated
}

print_path_guidance() {
  case "$path_status" in
    active) ;;
    updated)
      say "Added Oblive to PATH in $shell_profile."
      say "Open a new terminal or run: $path_line"
      ;;
    configured)
      say "Oblive is already configured in $shell_profile."
      say "To use it in this shell, run: $path_line"
      ;;
    skipped) say "To use Oblive in this shell, run: $path_line" ;;
  esac
}

mkdir -p "$config_home" "$bin_dir" "$(dirname "$install_dir")"
[ ! -e "$cli_link" ] || [ -L "$cli_link" ] || fail "$cli_link already exists and is not a symlink"

if [ -x "$install_dir/oblive" ] && [ -f "$install_dir/VERSION" ]; then
  printf '%s\n' "$download_base_url" > "$install_dir/.download-base-url"
  printf '%s\n' "$channel" > "$install_dir/.channel"
  ln -sfn "$install_dir/oblive" "$cli_link"
  if [ "$channel" = latest ]; then
    OBLIVE_HOME=$install_dir "$install_dir/oblive" update --latest
  else
    OBLIVE_HOME=$install_dir "$install_dir/oblive" update --version "$requested_version"
  fi
else
  [ ! -e "$install_dir" ] || fail "$install_dir exists but is not a valid Oblive installation"
  if [ "$channel" = latest ]; then
    curl --fail --location --silent --show-error --retry 3 \
      "$download_base_url/releases/latest" --output "$temporary_root/latest"
    release_version=$(normalize_version "$(sed -n '1p' "$temporary_root/latest")")
  else
    release_version=$requested_version
  fi
  release_url="$download_base_url/releases/$release_version"

  curl --fail --location --silent --show-error --retry 3 "$release_url/$asset_name" \
    --output "$temporary_root/$asset_name"
  curl --fail --location --silent --show-error --retry 3 "$release_url/$asset_name.sha256" \
    --output "$temporary_root/$asset_name.sha256"

  expected_checksum=$(awk 'NR == 1 { print $1 }' "$temporary_root/$asset_name.sha256")
  case "$expected_checksum" in *[!0-9a-f]* | "") fail "the release checksum is invalid" ;; esac
  [ "$(printf '%s' "$expected_checksum" | wc -c | tr -d ' ')" = 64 ] || fail "the release checksum is invalid"
  if command -v sha256sum >/dev/null 2>&1; then
    actual_checksum=$(sha256sum "$temporary_root/$asset_name" | awk '{ print $1 }')
  else
    actual_checksum=$(shasum -a 256 "$temporary_root/$asset_name" | awk '{ print $1 }')
  fi
  [ "$actual_checksum" = "$expected_checksum" ] || fail "the release checksum did not match"

  tar -tzf "$temporary_root/$asset_name" | while IFS= read -r entry; do
    case "$entry" in /* | ../* | */../* | */.. | *\\*) exit 1 ;; esac
  done || fail "the release archive contains an unsafe path"
  tar -tvzf "$temporary_root/$asset_name" | awk 'substr($1, 1, 1) !~ /^[-d]$/ { exit 1 }' ||
    fail "the release archive contains a link or special file"

  extract_dir="$temporary_root/extracted"
  mkdir "$extract_dir"
  tar -xzf "$temporary_root/$asset_name" -C "$extract_dir"
  bundled_version=$(sed -n '1p' "$extract_dir/VERSION")
  normalize_version "$bundled_version" >/dev/null
  [ "$bundled_version" = "$release_version" ] || fail "the release bundle does not match $release_version"

  mv "$extract_dir" "$install_dir"
  chmod 0755 "$install_dir/oblive"
  chmod 0644 "$install_dir/local-stack.schema.json"
  printf '%s\n' "$download_base_url" > "$install_dir/.download-base-url"
  printf '%s\n' "$channel" > "$install_dir/.channel"
  ln -sfn "$install_dir/oblive" "$cli_link"
fi

printf '%s\n' "$install_dir" > "$install_pointer"
OBLIVE_HOME=$install_dir "$install_dir/oblive" _init "$auth_mode"
configure_path

say "Installed Oblive $(sed -n '1p' "$install_dir/VERSION") in $install_dir."
print_path_guidance
if [ "$start_after_install" = true ]; then
  OBLIVE_HOME=$install_dir "$install_dir/oblive" start
else
  say "Configuration: $install_dir/local-stack.json"
  say "Start later with: oblive start"
fi
