#!/usr/bin/env sh
set -eu

DAY_SHIFT_DEFAULT_VERSION='0.1.20'
DAY_SHIFT_COMMAND_NAME='day-shift'
DAY_SHIFT_ORIGIN='https://tnsds.tech/downloads/'
DAY_SHIFT_VERSION=${DAY_SHIFT_VERSION:-$DAY_SHIFT_DEFAULT_VERSION}
DAY_SHIFT_INSTALL_DIR=${DAY_SHIFT_INSTALL_DIR:-$HOME/.local/day-shift}
DAY_SHIFT_BIN_DIR=${DAY_SHIFT_BIN_DIR:-$HOME/.local/bin}

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

need_command() {
  command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"
}

version_ge_20() {
  node_version=$(node --version 2>/dev/null | sed 's/^v//')
  node_major=${node_version%%.*}
  case "$node_major" in
    ''|*[!0-9]*) return 1 ;;
  esac
  [ "$node_major" -ge 20 ]
}

need_command node
need_command tar
need_command shasum
version_ge_20 || fail 'Day Shift requires system Node.js >=20.0.0 before installation changes are made.'

resolve_script_dir() {
  script_path=$0
  while [ -L "$script_path" ]; do
    script_dir=$(CDPATH= cd -- "$(dirname -- "$script_path")" && pwd)
    link_target=$(readlink "$script_path")
    case "$link_target" in
      /*) script_path=$link_target ;;
      *) script_path=$script_dir/$link_target ;;
    esac
  done
  CDPATH= cd -- "$(dirname -- "$script_path")" && pwd
}

validate_manifest_json() {
  manifest_candidate_path=$1
  manifest_candidate_source=$2
  node -e "const fs=require('fs'); const manifestPath=process.argv[1]; const manifestSource=process.argv[2]; let text=''; try { text=fs.readFileSync(manifestPath,'utf8'); } catch (error) { console.error('Failed to read downloaded release manifest.'); process.exit(1); } try { const parsed=JSON.parse(text); if(!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('shape'); } catch (error) { const trimmed=text.trimStart(); const likelyHtml=/^<!doctype html[>\s]/i.test(trimmed) || /^<html[>\s]/i.test(trimmed); const reason=likelyHtml ? 'The download returned HTML instead of JSON.' : 'The download did not contain valid JSON.'; console.error('Downloaded release manifest from ' + manifestSource + ' could not be parsed. ' + reason); console.error('Check that DAY_SHIFT_ORIGIN points at the published downloads directory and that release-manifest.json is reachable there.'); process.exit(1); }" "$manifest_candidate_path" "$manifest_candidate_source" || fail 'Downloaded release manifest is invalid.'
}

work_dir=$(mktemp -d 2>/dev/null || mktemp -d -t day-shift-install)
cleanup() { rm -rf "$work_dir"; }
trap cleanup EXIT INT TERM

script_dir=$(resolve_script_dir)
local_manifest_path="$script_dir/release-manifest.json"
manifest_path="$work_dir/release-manifest.json"
manifest_source=''
use_local_release_assets=false
if [ -f "$local_manifest_path" ]; then
  manifest_path="$local_manifest_path"
  manifest_source="$local_manifest_path"
  use_local_release_assets=true
else
  need_command curl
  manifest_url="${DAY_SHIFT_ORIGIN%/}/release-manifest.json"
  if [ "$DAY_SHIFT_VERSION" != latest ]; then
    manifest_url="${DAY_SHIFT_ORIGIN%/}/$DAY_SHIFT_VERSION/release-manifest.json"
  fi
  curl -fsSL "$manifest_url" -o "$manifest_path"
  manifest_source="$manifest_url"
fi
validate_manifest_json "$manifest_path" "$manifest_source"

resolved_version=$(node -e "const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); if(!m.version || m.version === 'latest') process.exit(2); process.stdout.write(m.version);" "$manifest_path")
archive_filename=$(node -e "const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); const a=(m.artifacts||[]).find((item)=>item.target==='macos-linux' && item.kind==='archive' && item.runtimeModel==='system-node'); if(!a) process.exit(2); process.stdout.write(a.filename);" "$manifest_path")
archive_sha256=$(node -e "const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); const a=(m.artifacts||[]).find((item)=>item.target==='macos-linux' && item.kind==='archive' && item.runtimeModel==='system-node'); if(!a || !/^[a-f0-9]{64}$/.test(a.sha256)) process.exit(2); process.stdout.write(a.sha256);" "$manifest_path")
manifest_command=$(node -e "const fs=require('fs'); const m=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); if(!m.command || !m.command.installedName) process.exit(2); process.stdout.write(m.command.installedName);" "$manifest_path")
[ "$manifest_command" = "$DAY_SHIFT_COMMAND_NAME" ] || DAY_SHIFT_COMMAND_NAME=$manifest_command

archive_path="$work_dir/$archive_filename"
extract_dir="$work_dir/extract"
stage_dir="$work_dir/stage"
if [ "$use_local_release_assets" = true ]; then
  [ "$DAY_SHIFT_VERSION" = latest ] || [ "$DAY_SHIFT_VERSION" = "$resolved_version" ] || fail "Local installer assets are for version $resolved_version, but DAY_SHIFT_VERSION requested $DAY_SHIFT_VERSION."
  archive_path="$script_dir/$archive_filename"
  [ -f "$archive_path" ] || fail "Local installer expected archive '$archive_filename' next to install.sh."
else
  archive_url="${DAY_SHIFT_ORIGIN%/}/$resolved_version/$archive_filename"
  curl -fsSL "$archive_url" -o "$archive_path"
fi
printf '%s  %s\n' "$archive_sha256" "$archive_path" | shasum -a 256 -c - >/dev/null
mkdir -p "$extract_dir" "$stage_dir"
tar -xzf "$archive_path" -C "$extract_dir"
payload_root=$(find "$extract_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)
[ -n "$payload_root" ] || fail 'Downloaded archive did not contain an installable payload directory.'
cp -R "$payload_root/." "$stage_dir/"
mkdir -p "$DAY_SHIFT_INSTALL_DIR" "$DAY_SHIFT_BIN_DIR"
rm -rf "$DAY_SHIFT_INSTALL_DIR.previous"
if [ -e "$DAY_SHIFT_INSTALL_DIR/current" ]; then
  mv "$DAY_SHIFT_INSTALL_DIR/current" "$DAY_SHIFT_INSTALL_DIR.previous"
fi
mv "$stage_dir" "$DAY_SHIFT_INSTALL_DIR/current"
ln -sf "$DAY_SHIFT_INSTALL_DIR/current/bin/$DAY_SHIFT_COMMAND_NAME" "$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME"
printf 'Installed Day Shift %s\n' "$resolved_version"
printf 'Executable: %s\n' "$DAY_SHIFT_BIN_DIR/$DAY_SHIFT_COMMAND_NAME"
printf 'Command: %s\n' "$DAY_SHIFT_COMMAND_NAME"
printf 'If %s is not found, run: export PATH="%s:$PATH"\n' "$DAY_SHIFT_COMMAND_NAME" "$DAY_SHIFT_BIN_DIR"
printf 'Persist PATH in your shell profile (for example ~/.zshrc or ~/.bashrc), then open a new shell or source the profile.\n'
