#!/usr/bin/env bash
set -euo pipefail

INSTALLER_VERSION="2026.05.04-python-first"
LABEL="online.freestock.daemon"
DEFAULT_INSTALL_ROOT="$HOME/Library/Application Support/Freestock"
INSTALL_ROOT="${FREESTOCK_INSTALL_ROOT:-$DEFAULT_INSTALL_ROOT}"
OPEN_BROWSER_ONCE=1
ORIGINS=(
  "https://freestock.online"
  "https://moscow001.freestock.online"
  "https://id-gw-01.freestock.online"
)

usage() {
  cat <<'USAGE'
Freestock macOS installer

Usage:
  install_freestock_macos.sh [--install-root PATH] [--no-open]

Environment:
  FREESTOCK_INSTALL_ROOT  Override install directory.
USAGE
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --install-root)
      INSTALL_ROOT="${2:-}"
      shift 2
      ;;
    --no-open)
      OPEN_BROWSER_ONCE=0
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Unknown argument: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

log() {
  printf '[freestock-macos] %s\n' "$*"
}

fail() {
  printf '[freestock-macos] ERROR: %s\n' "$*" >&2
  exit 1
}

have() {
  command -v "$1" >/dev/null 2>&1
}

if [[ "$(uname -s)" != "Darwin" ]]; then
  fail "This installer must run on macOS."
fi

INSTALL_ROOT="${INSTALL_ROOT/#\~/$HOME}"
INSTALL_ROOT="$(python3 -c 'import os,sys; print(os.path.abspath(os.path.expanduser(sys.argv[1])))' "$INSTALL_ROOT" 2>/dev/null || printf '%s' "$INSTALL_ROOT")"
PLIST_PATH="$HOME/Library/LaunchAgents/$LABEL.plist"

refresh_install_paths() {
  RUNTIME_DIR="$INSTALL_ROOT/runtime"
  WEBSERVER_DIR="$INSTALL_ROOT/WEBSERVER"
  EXTENSION_DIR="$INSTALL_ROOT/browser_extension_submitter"
  DAEMON_DIR="$INSTALL_ROOT/daemon"
}

refresh_install_paths

ensure_homebrew_package() {
  local package_name="$1"
  if ! have brew; then
    return 1
  fi
  log "Installing missing dependency with Homebrew: $package_name"
  brew install "$package_name"
}

find_python3() {
  if have python3; then
    command -v python3
    return 0
  fi
  if have brew; then
    ensure_homebrew_package "python@3.12" || true
    if have python3; then
      command -v python3
      return 0
    fi
    local brew_python
    brew_python="$(brew --prefix python@3.12 2>/dev/null)/bin/python3"
    if [[ -x "$brew_python" ]]; then
      printf '%s\n' "$brew_python"
      return 0
    fi
  fi
  return 1
}

PYTHON_BIN="$(find_python3 || true)"
if [[ -z "$PYTHON_BIN" ]]; then
  fail "python3 is required. Install Homebrew, then run: brew install python@3.12"
fi

"$PYTHON_BIN" - <<'PY' || fail "Python 3.10+ is required."
import sys
raise SystemExit(0 if sys.version_info >= (3, 10) else 1)
PY

INSTALL_ROOT="$("$PYTHON_BIN" -c 'import os,sys; print(os.path.abspath(os.path.expanduser(sys.argv[1])))' "$INSTALL_ROOT")"
refresh_install_paths

for required_tool in curl shasum ditto launchctl; do
  have "$required_tool" || fail "$required_tool is required on macOS."
done

missing_tools=()
for optional_tool in ffmpeg exiftool; do
  if ! have "$optional_tool"; then
    missing_tools+=("$optional_tool")
  fi
done
if [[ ${#missing_tools[@]} -gt 0 ]]; then
  if have brew; then
    for tool_name in "${missing_tools[@]}"; do
      ensure_homebrew_package "$tool_name"
    done
  else
    fail "Missing ${missing_tools[*]}. Install Homebrew, then run: brew install ffmpeg exiftool"
  fi
fi

download_first() {
  local target_path="$1"
  local relative_path="$2"
  local url
  rm -f "$target_path"
  for origin in "${ORIGINS[@]}"; do
    url="${origin}${relative_path}"
    log "Downloading $url"
    if curl -fL --connect-timeout 15 --max-time 900 --retry 2 --retry-delay 2 -o "$target_path.tmp" "$url"; then
      mv "$target_path.tmp" "$target_path"
      return 0
    fi
    rm -f "$target_path.tmp"
  done
  return 1
}

download_from_url_list() {
  local target_path="$1"
  local url_list_path="$2"
  local url
  rm -f "$target_path"
  while IFS= read -r url; do
    [[ -n "$url" ]] || continue
    log "Downloading payload $url"
    if curl -fL --connect-timeout 15 --max-time 1200 --retry 2 --retry-delay 2 -o "$target_path.tmp" "$url"; then
      mv "$target_path.tmp" "$target_path"
      return 0
    fi
    rm -f "$target_path.tmp"
  done < "$url_list_path"
  return 1
}

stop_launch_agent() {
  local gui_domain="gui/$(id -u)"
  launchctl bootout "$gui_domain/$LABEL" >/dev/null 2>&1 || true
  launchctl bootout "$gui_domain" "$PLIST_PATH" >/dev/null 2>&1 || true
  launchctl unload -w "$PLIST_PATH" >/dev/null 2>&1 || true
}

TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/freestock-macos-install.XXXXXX")"
cleanup() {
  rm -rf "$TMP_DIR"
}
trap cleanup EXIT

mkdir -p "$INSTALL_ROOT" "$RUNTIME_DIR"
INSTALL_LOG="$RUNTIME_DIR/install.log"
touch "$INSTALL_LOG"

log "Install root: $INSTALL_ROOT" | tee -a "$INSTALL_LOG"
log "Python: $PYTHON_BIN ($("$PYTHON_BIN" --version 2>&1))" | tee -a "$INSTALL_LOG"

MANIFEST_PATH="$TMP_DIR/freestock-bootstrap-dependencies.json"
download_first "$MANIFEST_PATH" "/updates/freestock-bootstrap-dependencies.json" || fail "Unable to download bootstrap dependencies manifest."

ARCH="$(uname -m)"
DEP_JSON="$TMP_DIR/web_payload_dependency.json"
URL_LIST="$TMP_DIR/web_payload_urls.txt"
"$PYTHON_BIN" - "$MANIFEST_PATH" "$ARCH" "$DEP_JSON" "$URL_LIST" <<'PY'
import json
import sys
from pathlib import Path

manifest_path, arch, dep_path, urls_path = sys.argv[1:5]
payload = json.loads(Path(manifest_path).read_text(encoding="utf-8-sig"))
section = "darwin_arm64" if arch == "arm64" else "darwin_amd64"
deps = payload.get(section) or payload.get("darwin_amd64") or payload.get("darwin_arm64") or {}
dep = deps.get("freestock_webserver_payload")
if not isinstance(dep, dict):
    raise SystemExit(f"freestock_webserver_payload is missing in {section}")
urls = []
for value in [dep.get("url_string"), *(dep.get("urls_list") or [])]:
    value = str(value or "").strip()
    if value and value not in urls:
        urls.append(value)
if not urls:
    raise SystemExit("freestock_webserver_payload has no URLs")
Path(dep_path).write_text(json.dumps(dep, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
Path(urls_path).write_text("\n".join(urls) + "\n", encoding="utf-8")
print(dep.get("sha256_string", ""))
PY

DEP_SHA="$("$PYTHON_BIN" -c 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8"))["sha256_string"])' "$DEP_JSON")"
DEP_SIZE="$("$PYTHON_BIN" -c 'import json,sys; print(int(json.load(open(sys.argv[1], encoding="utf-8"))["size_int"]))' "$DEP_JSON")"
PAYLOAD_ZIP="$TMP_DIR/freestock-webserver-payload.zip"
download_from_url_list "$PAYLOAD_ZIP" "$URL_LIST" || fail "Unable to download freestock-webserver-payload.zip."

ACTUAL_SHA="$(shasum -a 256 "$PAYLOAD_ZIP" | awk '{print $1}')"
ACTUAL_SIZE="$(wc -c < "$PAYLOAD_ZIP" | tr -d ' ')"
if [[ "$ACTUAL_SHA" != "$DEP_SHA" ]]; then
  fail "Payload SHA mismatch: expected $DEP_SHA, got $ACTUAL_SHA"
fi
if [[ "$ACTUAL_SIZE" != "$DEP_SIZE" ]]; then
  fail "Payload size mismatch: expected $DEP_SIZE, got $ACTUAL_SIZE"
fi

EXTRACT_DIR="$TMP_DIR/payload"
mkdir -p "$EXTRACT_DIR"
ditto -x -k "$PAYLOAD_ZIP" "$EXTRACT_DIR"
[[ -d "$EXTRACT_DIR/WEBSERVER" ]] || fail "Payload does not contain WEBSERVER/."
[[ -d "$EXTRACT_DIR/browser_extension_submitter" ]] || fail "Payload does not contain browser_extension_submitter/."

stop_launch_agent

if [[ -f "$WEBSERVER_DIR/webserver.json" ]]; then
  cp "$WEBSERVER_DIR/webserver.json" "$TMP_DIR/webserver.json.keep"
fi

rm -rf "$INSTALL_ROOT/WEBSERVER.next" "$INSTALL_ROOT/browser_extension_submitter.next"
mv "$EXTRACT_DIR/WEBSERVER" "$INSTALL_ROOT/WEBSERVER.next"
mv "$EXTRACT_DIR/browser_extension_submitter" "$INSTALL_ROOT/browser_extension_submitter.next"

if [[ -f "$TMP_DIR/webserver.json.keep" ]]; then
  cp "$TMP_DIR/webserver.json.keep" "$INSTALL_ROOT/WEBSERVER.next/webserver.json"
elif [[ ! -f "$INSTALL_ROOT/WEBSERVER.next/webserver.json" ]]; then
  cat > "$INSTALL_ROOT/WEBSERVER.next/webserver.json" <<'JSON'
{
  "host": "127.0.0.1",
  "port": 7332,
  "dashboard_path": "/webserver/",
  "gateway_base_url_string": "https://freestock.online",
  "gateway_endpoints_list": [
    {
      "gateway_key_string": "origin",
      "gateway_name_string": "Europe / Origin",
      "gateway_region_string": "eu",
      "base_url_string": "https://freestock.online",
      "priority_int": 100
    },
    {
      "gateway_key_string": "jakarta",
      "gateway_name_string": "Jakarta",
      "gateway_region_string": "id",
      "base_url_string": "https://id-gw-01.freestock.online",
      "priority_int": 200
    },
    {
      "gateway_key_string": "moscow001",
      "gateway_name_string": "Moscow 001",
      "gateway_region_string": "ru-msk",
      "base_url_string": "https://moscow001.freestock.online",
      "priority_int": 300
    }
  ],
  "node_name_string": "Freestock Desktop Node",
  "media_roots": [],
  "user_name": "",
  "sync_roots": ["."],
  "torrent_ignore_file": "webserver.torrentignore",
  "torrent": {
    "enabled": false,
    "seed": false,
    "torrent_file": "webserver.torrent",
    "channel_file": "channel.json",
    "listen_port": 42069,
    "bootstrap_peers": [],
    "poll_seconds": 15,
    "no_dht": true
  },
  "updates": {
    "enabled": true,
    "channel_url_string": "https://freestock.online/updates/freestock-node-channel.json",
    "poll_seconds_int": 10
  },
  "daemon_updates": {
    "enabled": false,
    "channel_url_string": "https://freestock.online/updates/freestock-daemon-channel.json",
    "poll_seconds_int": 30
  }
}
JSON
fi

rm -rf "$WEBSERVER_DIR.prev" "$EXTENSION_DIR.prev"
if [[ -d "$WEBSERVER_DIR" ]]; then
  mv "$WEBSERVER_DIR" "$WEBSERVER_DIR.prev"
fi
if [[ -d "$EXTENSION_DIR" ]]; then
  mv "$EXTENSION_DIR" "$EXTENSION_DIR.prev"
fi
mv "$INSTALL_ROOT/WEBSERVER.next" "$WEBSERVER_DIR"
mv "$INSTALL_ROOT/browser_extension_submitter.next" "$EXTENSION_DIR"
rm -rf "$WEBSERVER_DIR.prev" "$EXTENSION_DIR.prev"

log "Creating Python virtual environment" | tee -a "$INSTALL_LOG"
rm -rf "$WEBSERVER_DIR/.venv"
"$PYTHON_BIN" -m venv "$WEBSERVER_DIR/.venv"
"$WEBSERVER_DIR/.venv/bin/python" -m pip install --upgrade pip >>"$INSTALL_LOG" 2>&1
"$WEBSERVER_DIR/.venv/bin/python" -m pip install -r "$WEBSERVER_DIR/requirements.txt" >>"$INSTALL_LOG" 2>&1

mkdir -p "$DAEMON_DIR"
DAEMON_SCRIPT="$DAEMON_DIR/freestock_macos_daemon.py"
download_first "$DAEMON_SCRIPT" "/installers/freestock_macos_daemon.py" || fail "Unable to download freestock_macos_daemon.py."
chmod 0755 "$DAEMON_SCRIPT"

"$WEBSERVER_DIR/.venv/bin/python" - "$INSTALL_ROOT" "$INSTALLER_VERSION" <<'PY'
import json
import platform
import sys
from datetime import datetime, timezone
from pathlib import Path

root = Path(sys.argv[1])
payload = {
    "install_root": str(root),
    "installer_version": sys.argv[2],
    "python_kind": "system",
    "python_version": platform.python_version(),
    "installed_at_utc_string": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"),
    "platform_string": "macos-python-first",
}
(root / "install.json").write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
PY

mkdir -p "$(dirname "$PLIST_PATH")"
"$WEBSERVER_DIR/.venv/bin/python" - "$PLIST_PATH" "$WEBSERVER_DIR/.venv/bin/python" "$DAEMON_SCRIPT" "$INSTALL_ROOT" "$RUNTIME_DIR" <<'PY'
import plistlib
import sys
from pathlib import Path

plist_path = Path(sys.argv[1])
python_path = sys.argv[2]
daemon_path = sys.argv[3]
install_root = sys.argv[4]
runtime_dir = Path(sys.argv[5])
program_args = [python_path, daemon_path, "--install-root", install_root]
payload = {
    "Label": "online.freestock.daemon",
    "ProgramArguments": program_args,
    "RunAtLoad": True,
    "KeepAlive": True,
    "WorkingDirectory": install_root,
    "StandardOutPath": str(runtime_dir / "launchd.stdout.log"),
    "StandardErrorPath": str(runtime_dir / "launchd.stderr.log"),
    "EnvironmentVariables": {
        "FREESTOCK_INSTALL_ROOT": install_root,
        "PYTHONUNBUFFERED": "1",
        "PATH": "/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin",
    },
}
plist_path.parent.mkdir(parents=True, exist_ok=True)
with plist_path.open("wb") as fh:
    plistlib.dump(payload, fh, sort_keys=False)
PY

GUI_DOMAIN="gui/$(id -u)"
launchctl bootstrap "$GUI_DOMAIN" "$PLIST_PATH" >/dev/null 2>&1 || launchctl load -w "$PLIST_PATH"
launchctl enable "$GUI_DOMAIN/$LABEL" >/dev/null 2>&1 || true
launchctl kickstart -k "$GUI_DOMAIN/$LABEL" >/dev/null 2>&1 || launchctl start "$LABEL" || true

if [[ "$OPEN_BROWSER_ONCE" == "1" ]]; then
  open "http://127.0.0.1:7332/webserver/" >/dev/null 2>&1 || true
fi

log "Installed Freestock Desktop Node for macOS."
log "Dashboard: http://127.0.0.1:7332/webserver/"
log "LaunchAgent: $PLIST_PATH"
log "Install log: $INSTALL_LOG"
log "Daemon log: $RUNTIME_DIR/daemon.log"
