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

RENDEZVOUS_URL="${TUCDESK_RENDEZVOUS_URL:-https://rendezvous-stag.tucdesk.app}"
SERVER_CERT_FINGERPRINT="${TUCDESK_SERVER_CERT_FINGERPRINT:-}"
DOWNLOAD_BASE_URL="${TUCDESK_DOWNLOAD_BASE_URL:-https://get-stag.tucdesk.app/downloads}"
INSTALL_DIR="${TUCDESK_INSTALL_DIR:-/usr/local/bin}"
CONFIG_DIR="${TUCDESK_CONFIG_DIR:-/etc/tucdesk}"
VERSION="${TUCDESK_VERSION:-latest}"
GITHUB_REPO="${TUCDESK_GITHUB_REPO:-portmytech/TucDesk-original}"
INSTALL_DRY_RUN="${TUCDESK_INSTALL_DRY_RUN:-0}"
LOCAL_BINARY="${TUCDESK_LOCAL_BINARY:-}"
LOCAL_BUILD="${TUCDESK_LOCAL_BUILD:-0}"
SERVICE_OUTPUT_DIR="${TUCDESK_SERVICE_OUTPUT_DIR:-}"

OS=""
ARCH=""
FILENAME=""
DOWNLOAD_URL=""
CHECKSUM_URL=""
INSTALL_BIN=""
BINARY_PATH=""
TMP_DIR=""
SUDO_CMD=""
INSTALL_CHANNEL="source"

fail() {
  echo "ERROR: $*" >&2
  exit 1
}

step_line() {
  local label="$1"
  local status="$2"
  printf "%s%s\n" "$label" "$status"
}

setup_privilege_runner() {
  if [ "$INSTALL_DRY_RUN" = "1" ]; then
    SUDO_CMD=""
    return
  fi
  if [ "${EUID:-$(id -u)}" -eq 0 ]; then
    SUDO_CMD=""
    return
  fi
  if ! command -v sudo >/dev/null 2>&1; then
    fail "sudo is required for system install"
  fi
  if sudo -n true 2>/dev/null; then
    SUDO_CMD="sudo -n"
    return
  fi
  fail "sudo requires a password in this environment; rerun as root to keep install non-interactive"
}

as_root() {
  if [ -n "$SUDO_CMD" ]; then
    $SUDO_CMD "$@"
  else
    "$@"
  fi
}

resolve_rendezvous_url() {
  if [ -n "$RENDEZVOUS_URL" ]; then
    return
  fi
  if [ "$INSTALL_CHANNEL" = "release" ]; then
    RENDEZVOUS_URL="https://rendezvous.tucdesk.app"
  else
    RENDEZVOUS_URL="https://rendezvous-stag.tucdesk.app"
  fi
}

use_hosted_downloads() {
  [ -n "$DOWNLOAD_BASE_URL" ] &&
    [ "$VERSION" = "latest" ] &&
    [ "$GITHUB_REPO" = "portmytech/TucDesk-original" ]
}

hosted_download_url() {
  case "$OS/$ARCH" in
    linux/amd64) printf '%s\n' "${DOWNLOAD_BASE_URL%/}/linux/tucdesk-agent" ;;
    linux/arm64) printf '%s\n' "${DOWNLOAD_BASE_URL%/}/linux/arm64/tucdesk-agent" ;;
    darwin/amd64) printf '%s\n' "${DOWNLOAD_BASE_URL%/}/darwin/amd64/tucdesk-agent" ;;
    darwin/arm64) printf '%s\n' "${DOWNLOAD_BASE_URL%/}/darwin/arm64/tucdesk-agent" ;;
    windows/amd64) printf '%s\n' "${DOWNLOAD_BASE_URL%/}/windows/tucdesk-agent.zip" ;;
    *) return 1 ;;
  esac
}

hosted_checksum_url() {
  printf '%s\n' "${DOWNLOAD_BASE_URL%/}/checksums.txt"
}

detect_platform() {
  OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
  ARCH="$(uname -m)"

  case "$OS" in
    darwin) OS="darwin" ;;
    linux) OS="linux" ;;
    msys*|cygwin*|mingw*) OS="windows" ;;
    *) fail "Unsupported OS: $OS" ;;
  esac

  case "$ARCH" in
    x86_64) ARCH="amd64" ;;
    aarch64|arm64) ARCH="arm64" ;;
    *) fail "Unsupported architecture: $ARCH" ;;
  esac
}

build_from_source() {
  if ! command -v go >/dev/null 2>&1; then
    echo ""
    echo "  ERROR: Go is not installed and no pre-built binary is available."
    echo "  Install Go from: https://go.dev/dl/"
    echo "  Then re-run this installer."
    exit 1
  fi

  echo "  Go found: $(go version)"
  echo "  Building tucdesk from source..."
  INSTALL_CHANNEL="source"

  local repo_root binary_name version_tag build_date
  repo_root=""
  if [ -f "go.mod" ] && grep -q "^module tucdesk" go.mod 2>/dev/null; then
    repo_root="$(pwd)"
  elif [ -f "../go.mod" ] && grep -q "^module tucdesk" ../go.mod 2>/dev/null; then
    repo_root="$(cd .. && pwd)"
  fi

  if [ -z "$repo_root" ]; then
    echo ""
    echo "  ERROR: Cannot find TucDesk source code."
    echo "  Run this script from inside the TucDesk repo directory."
    echo "  Or wait for an official release at: https://github.com/${GITHUB_REPO}/releases"
    exit 1
  fi

  cd "$repo_root"

  binary_name="tucdesk"
  [ "$OS" = "windows" ] && binary_name="tucdesk.exe"
  version_tag="$(git describe --tags --always 2>/dev/null || echo dev)"
  build_date="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

  GOOS="$OS" GOARCH="$ARCH" go build \
    -ldflags="-s -w -X tucdesk/shared/config.Version=${version_tag} -X tucdesk/shared/config.BuildDate=${build_date}" \
    -o "$TMP_DIR/$binary_name" \
    ./cmd/tucdesk-agent

  BINARY_PATH="$TMP_DIR/$binary_name"
  echo "  Built successfully from source."
}

download_or_build() {
  echo ""
  echo "  Downloading TucDesk..."

  if [ -n "$LOCAL_BINARY" ]; then
    [ -f "$LOCAL_BINARY" ] || fail "TUCDESK_LOCAL_BINARY points to missing file: $LOCAL_BINARY"
    local tmp_local
    tmp_local="$TMP_DIR/tucdesk-local"
    cp "$LOCAL_BINARY" "$tmp_local"
    chmod +x "$tmp_local"
    BINARY_PATH="$tmp_local"
    INSTALL_CHANNEL="source"
    echo "  Using local binary override."
    return
  fi

  if [ "$LOCAL_BUILD" = "1" ]; then
    echo "  Local build mode enabled."
    build_from_source
    return
  fi

  if [ "$OS" = "windows" ]; then
    FILENAME="tucdesk-${OS}-${ARCH}.zip"
  else
    FILENAME="tucdesk-${OS}-${ARCH}.tar.gz"
  fi

  if use_hosted_downloads; then
    DOWNLOAD_URL="$(hosted_download_url)" || fail "unsupported hosted download target for ${OS}/${ARCH}"
    CHECKSUM_URL="$(hosted_checksum_url)"
    if curl -fsSL --max-time 10 "$DOWNLOAD_URL" -o "$TMP_DIR/$FILENAME" 2>/dev/null; then
      echo "  Downloaded from hosted download path."
      BINARY_PATH="$TMP_DIR/$FILENAME"
      INSTALL_CHANNEL="release"
      return
    fi
    echo "  Hosted download path unavailable. Falling back to GitHub release..."
    DOWNLOAD_URL=""
    CHECKSUM_URL=""
  fi

  if [ "$VERSION" = "latest" ]; then
    DOWNLOAD_URL="https://github.com/${GITHUB_REPO}/releases/latest/download/${FILENAME}"
  else
    DOWNLOAD_URL="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/${FILENAME}"
  fi

  if curl -fsSL --max-time 10 "$DOWNLOAD_URL" -o "$TMP_DIR/$FILENAME" 2>/dev/null; then
    echo "  Downloaded from GitHub release."
    BINARY_PATH="$TMP_DIR/$FILENAME"
    INSTALL_CHANNEL="release"
  else
    echo "  No release found. Building from source..."
    build_from_source
  fi
}

extract_release_binary() {
  local extracted
  extracted=""
  if [ "$OS" = "windows" ]; then
    unzip -j -o "$BINARY_PATH" "tucdesk.exe" -d "$TMP_DIR" >/dev/null 2>&1 || fail "failed to extract tucdesk.exe"
    extracted="$TMP_DIR/tucdesk.exe"
  else
    tar -xzf "$BINARY_PATH" -C "$TMP_DIR" "tucdesk" >/dev/null 2>&1 || fail "failed to extract tucdesk"
    extracted="$TMP_DIR/tucdesk"
  fi
  [ -f "$extracted" ] || fail "extracted binary not found: $extracted"
  chmod +x "$extracted"
  INSTALL_BIN="$extracted"
}

download_binary() {
  download_or_build
  [ -n "$BINARY_PATH" ] || fail "No installer binary resolved"
  INSTALL_BIN="$BINARY_PATH"
  local checksum_url checksums expected actual
  if [[ "$BINARY_PATH" == "$TMP_DIR/tucdesk-${OS}-${ARCH}.tar.gz" || "$BINARY_PATH" == "$TMP_DIR/tucdesk-${OS}-${ARCH}.zip" ]]; then
    checksum_url="${CHECKSUM_URL:-${DOWNLOAD_URL%/*}/checksums.txt}"
    checksums="$(curl -fsSL "$checksum_url" 2>/dev/null || true)"
    if [ -n "$checksums" ]; then
      expected="$(echo "$checksums" | grep " ${FILENAME}$" | awk '{print $1}' | head -n 1)"
      if [ -n "$expected" ]; then
        actual="$( (sha256sum "$BINARY_PATH" 2>/dev/null || shasum -a 256 "$BINARY_PATH") | awk '{print $1}')"
        [ "$expected" = "$actual" ] || fail "checksum mismatch for ${FILENAME}"
      fi
    fi
    extract_release_binary
    return
  fi

  if [[ "$BINARY_PATH" == "$TMP_DIR/tucdesk" || "$BINARY_PATH" == "$TMP_DIR/tucdesk.exe" || "$BINARY_PATH" == "$TMP_DIR/tucdesk-local" ]]; then
    chmod +x "$BINARY_PATH"
    INSTALL_BIN="$BINARY_PATH"
    return
  fi

  fail "unsupported installer artifact format: $BINARY_PATH"
}

handle_upgrade() {
  if command -v tucdesk >/dev/null 2>&1; then
    if [ "$OS" = "darwin" ]; then
      as_root launchctl stop io.tucdesk.agent >/dev/null 2>&1 || true
    elif [ "$OS" = "linux" ]; then
      as_root systemctl stop tucdesk-agent >/dev/null 2>&1 || true
    fi
  fi
}

install_binary() {
  as_root mkdir -p "$INSTALL_DIR"
  as_root mv "$INSTALL_BIN" "$INSTALL_DIR/tucdesk"
  as_root chmod 755 "$INSTALL_DIR/tucdesk"
  as_root tee "$INSTALL_DIR/tucdesk-tui" >/dev/null <<EOF
#!/usr/bin/env bash
exec "${INSTALL_DIR}/tucdesk" --tui "\$@"
EOF
  as_root chmod 755 "$INSTALL_DIR/tucdesk-tui"
  as_root mkdir -p "$CONFIG_DIR"
  as_root chmod 755 "$CONFIG_DIR"
}

write_config_and_identity() {
  as_root env TUCDESK_CONFIG_DIR="$CONFIG_DIR" TUCDESK_SERVER_CERT_FINGERPRINT="$SERVER_CERT_FINGERPRINT" "$INSTALL_DIR/tucdesk" --init-only --rendezvous-url "$RENDEZVOUS_URL" --config-dir "$CONFIG_DIR" >/dev/null 2>&1
}

install_service_macos() {
  local plist plist_dir
  if [ "$INSTALL_DRY_RUN" = "1" ]; then
    plist_dir="${SERVICE_OUTPUT_DIR:-$(pwd)/.installer-artifacts}"
    mkdir -p "$plist_dir"
    plist="${plist_dir}/io.tucdesk.agent.plist"
  else
    plist="/Library/LaunchDaemons/io.tucdesk.agent.plist"
    as_root mkdir -p /var/log/tucdesk
  fi

  if [ "$INSTALL_DRY_RUN" = "1" ]; then
    cat >"$plist" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>io.tucdesk.agent</string>
  <key>ProgramArguments</key>
  <array>
    <string>${INSTALL_DIR}/tucdesk</string>
    <string>--agent</string>
    <string>--config-dir</string>
    <string>${CONFIG_DIR}</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/var/log/tucdesk/agent.log</string>
  <key>StandardErrorPath</key>
  <string>/var/log/tucdesk/agent.error.log</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>TUCDESK_CONFIG_DIR</key>
    <string>${CONFIG_DIR}</string>
    <key>TUCDESK_RENDEZVOUS_URL</key>
    <string>${RENDEZVOUS_URL}</string>
    <key>TUCDESK_SERVER_CERT_FINGERPRINT</key>
    <string>${SERVER_CERT_FINGERPRINT}</string>
  </dict>
</dict>
</plist>
EOF
  else
    as_root tee "$plist" >/dev/null <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>io.tucdesk.agent</string>
  <key>ProgramArguments</key>
  <array>
    <string>${INSTALL_DIR}/tucdesk</string>
    <string>--agent</string>
    <string>--config-dir</string>
    <string>${CONFIG_DIR}</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <true/>
  <key>StandardOutPath</key>
  <string>/var/log/tucdesk/agent.log</string>
  <key>StandardErrorPath</key>
  <string>/var/log/tucdesk/agent.error.log</string>
  <key>EnvironmentVariables</key>
  <dict>
    <key>TUCDESK_CONFIG_DIR</key>
    <string>${CONFIG_DIR}</string>
    <key>TUCDESK_RENDEZVOUS_URL</key>
    <string>${RENDEZVOUS_URL}</string>
    <key>TUCDESK_SERVER_CERT_FINGERPRINT</key>
    <string>${SERVER_CERT_FINGERPRINT}</string>
  </dict>
</dict>
</plist>
EOF
  fi

  if [ "$INSTALL_DRY_RUN" = "1" ]; then
    return
  fi

  as_root launchctl unload "$plist" >/dev/null 2>&1 || true
  as_root launchctl load "$plist"
  as_root launchctl start io.tucdesk.agent >/dev/null 2>&1 || true
  if ! as_root launchctl list | grep -q io.tucdesk.agent; then
    fail "launchd service failed to start; check /var/log/tucdesk/agent.error.log"
  fi
}

install_service_linux() {
  if ! command -v systemctl >/dev/null 2>&1; then
    fail "systemctl is required for Linux service installation"
  fi

  local service_path service_dir
  if [ "$INSTALL_DRY_RUN" = "1" ]; then
    service_dir="${SERVICE_OUTPUT_DIR:-$(pwd)/.installer-artifacts}"
    mkdir -p "$service_dir"
    service_path="${service_dir}/tucdesk-agent.service"
  else
    service_path="/etc/systemd/system/tucdesk-agent.service"
  fi

  if [ "$INSTALL_DRY_RUN" != "1" ]; then
    if ! id tucdesk >/dev/null 2>&1; then
      as_root useradd --system --no-create-home --shell /bin/false tucdesk
    fi
    as_root chown -R tucdesk:tucdesk "$CONFIG_DIR"
  fi

  if [ "$INSTALL_DRY_RUN" = "1" ]; then
    cat >"$service_path" <<EOF
[Unit]
Description=TucDesk Agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=${INSTALL_DIR}/tucdesk --agent --config-dir ${CONFIG_DIR}
Restart=always
RestartSec=5
User=tucdesk
Group=tucdesk
Environment=TUCDESK_CONFIG_DIR=${CONFIG_DIR}
Environment=TUCDESK_RENDEZVOUS_URL=${RENDEZVOUS_URL}
Environment=TUCDESK_SERVER_CERT_FINGERPRINT=${SERVER_CERT_FINGERPRINT}
StandardOutput=journal
StandardError=journal
SyslogIdentifier=tucdesk-agent

[Install]
WantedBy=multi-user.target
EOF
    return
  fi

  as_root tee "$service_path" >/dev/null <<EOF
[Unit]
Description=TucDesk Agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=${INSTALL_DIR}/tucdesk --agent --config-dir ${CONFIG_DIR}
Restart=always
RestartSec=5
User=tucdesk
Group=tucdesk
Environment=TUCDESK_CONFIG_DIR=${CONFIG_DIR}
Environment=TUCDESK_RENDEZVOUS_URL=${RENDEZVOUS_URL}
Environment=TUCDESK_SERVER_CERT_FINGERPRINT=${SERVER_CERT_FINGERPRINT}
StandardOutput=journal
StandardError=journal
SyslogIdentifier=tucdesk-agent

[Install]
WantedBy=multi-user.target
EOF

  as_root systemctl daemon-reload
  as_root systemctl enable tucdesk-agent --quiet
  as_root systemctl restart tucdesk-agent
  as_root systemctl is-active --quiet tucdesk-agent || fail "tucdesk-agent failed to start; run journalctl -u tucdesk-agent -n 50"
}

install_service_windows() {
  local nssm_dir nssm_exe
  nssm_dir="/c/Program Files/nssm"
  nssm_exe="${nssm_dir}/nssm.exe"

  if [ ! -f "$nssm_exe" ]; then
    curl -fsSL "https://nssm.cc/release/nssm-2.24.zip" -o /tmp/nssm.zip
    powershell.exe -NoProfile -Command "Expand-Archive -Path /tmp/nssm.zip -DestinationPath /tmp/nssm -Force" >/dev/null
    mkdir -p "$nssm_dir"
    cp "/tmp/nssm/nssm-2.24/win64/nssm.exe" "$nssm_exe"
  fi

  "$nssm_exe" install TucDesk "${INSTALL_DIR}/tucdesk.exe" --agent
  "$nssm_exe" set TucDesk Start SERVICE_AUTO_START
  "$nssm_exe" set TucDesk AppStdout "${PROGRAMDATA:-C:/ProgramData}/TucDesk/logs/agent.log"
  "$nssm_exe" set TucDesk AppStderr "${PROGRAMDATA:-C:/ProgramData}/TucDesk/logs/agent.error.log"
  cmd.exe /c "net start TucDesk" >/dev/null
  cmd.exe /c "sc query TucDesk | findstr RUNNING" >/dev/null || fail "TucDesk windows service failed to start"
}

install_service() {
  if [ "$OS" = "darwin" ]; then
    install_service_macos
  elif [ "$OS" = "linux" ]; then
    install_service_linux
  else
    install_service_windows
  fi
}

wait_for_online() {
  local attempts
  attempts=0
  while [ "$attempts" -lt 15 ]; do
    if as_root env TUCDESK_CONFIG_DIR="$CONFIG_DIR" TUCDESK_RENDEZVOUS_URL="$RENDEZVOUS_URL" "$INSTALL_DIR/tucdesk" --status-json --config-dir "$CONFIG_DIR" 2>/dev/null | grep -q '"online":true'; then
      return 0
    fi
    sleep 1
    attempts=$((attempts + 1))
  done
  return 1
}

print_success() {
  local peer_id pairing_key rv_host status_label rv_mark resolved_path status_cmd
  status_label="$1"
  rv_mark="$2"
  peer_id="$(as_root env TUCDESK_CONFIG_DIR="$CONFIG_DIR" "$INSTALL_DIR/tucdesk" --peer-id --config-dir "$CONFIG_DIR" 2>/dev/null || echo "unknown")"
  pairing_key="$(as_root cat "$CONFIG_DIR/pairing.key" 2>/dev/null | tr -d '\n' || echo "not found")"
  rv_host="$(echo "$RENDEZVOUS_URL" | sed -E 's#^https?://##' | sed -E 's#/.*$##')"
  resolved_path="$(command -v tucdesk 2>/dev/null || true)"

  if [ "$CONFIG_DIR" = "/etc/tucdesk" ]; then
    status_cmd="sudo ${INSTALL_DIR}/tucdesk status --config-dir ${CONFIG_DIR}"
  else
    status_cmd="${INSTALL_DIR}/tucdesk status --config-dir ${CONFIG_DIR}"
  fi

  echo ""
  echo "  ╔══════════════════════════════════════════════════╗"
  echo "  ║  TucDesk installed and running.                  ║"
  echo "  ╠══════════════════════════════════════════════════╣"
  printf "  ║  Your Peer ID:   %-30s║\n" "$peer_id"
  printf "  ║  Pairing Key:    %-30s║\n" "$pairing_key"
  printf "  ║  Status:         %-30s║\n" "$status_label"
  printf "  ║  Rendezvous:     %-24s %-2s    ║\n" "$rv_host" "$rv_mark"
  echo "  ╠══════════════════════════════════════════════════╣"
  echo "  ║  Share your Peer ID with anyone who needs        ║"
  echo "  ║  to connect to this machine.                     ║"
  echo "  ║                                                  ║"
  printf "  ║  Run:  %-40s║\n" "$status_cmd"
  echo "  ╚══════════════════════════════════════════════════╝"

  if [ -n "$resolved_path" ] && [ "$resolved_path" != "$INSTALL_DIR/tucdesk" ]; then
    echo ""
    echo "  Warning: your shell currently resolves 'tucdesk' to:"
    echo "    $resolved_path"
    echo "  The newly installed binary is at:"
    echo "    $INSTALL_DIR/tucdesk"
    echo "  Run 'hash -r' or update PATH if you want plain 'tucdesk' to use the new install."
  fi
}

main() {
  TMP_DIR="$(mktemp -d)"
  trap 'rm -rf "$TMP_DIR"' EXIT
  detect_platform
  setup_privilege_runner
  handle_upgrade

  download_binary
  resolve_rendezvous_url
  [ -n "$RENDEZVOUS_URL" ] || fail "RENDEZVOUS_URL cannot be empty"
  step_line "Downloading TucDesk...        " "████████████ 100%"

  install_binary
  step_line "Installing...                 " "done"

  write_config_and_identity
  step_line "Generating identity...        " "done"

  install_service
  step_line "Starting agent service...     " "done"

  if wait_for_online; then
    step_line "Connecting to rendezvous...   " "done"
    print_success "Online" "✓"
  else
    step_line "Connecting to rendezvous...   " "timeout"
    print_success "Pending verification" "?"
  fi
}

main "$@"
