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

APP_NAME="okclawpro"
SERVICE_NAME="${OKCLAW_SERVICE_NAME:-okclawpro-server}"
VERSION="${OKCLAW_VERSION:-1.0.3}"
INSTALL_DIR="${OKCLAW_INSTALL_DIR:-/opt/okclaw/linux}"
SERVICE_PORT=9100
PORT="${OKCLAW_PORT:-${SERVICE_PORT}}"
PORT_WAS_PROVIDED=false
[ -n "${OKCLAW_PORT:-}" ] && PORT_WAS_PROVIDED=true
HOST="${OKCLAW_HOST:-0.0.0.0}"
PACKAGE_BASE_URL="${OKCLAW_PACKAGE_BASE_URL:-https://okclaw-client.dftianyi.top/packages}"
PACKAGE_URL="${OKCLAW_PACKAGE_URL:-}"
PACKAGE_API_BASE="${OKCLAW_PACKAGE_API_BASE:-https://tycloud-api.dftianyi.com/api/blade-resource/client-software/endpoint}"
PACKAGE_API_ENABLED="${OKCLAW_PACKAGE_API_ENABLED:-true}"
SOFTWARE_CODE="${OKCLAW_SOFTWARE_CODE:-ffworkai}"
LINUX_PLATFORM_TYPE="${OKCLAW_LINUX_PLATFORM_TYPE:-3}"
WEBUI_WS_URL="${CRAFT_WEBUI_WS_URL:-}"
PUBLIC_SIGNALING_URL="${OKCLAW_PUBLIC_SIGNALING_URL:-wss://okclaw-client.dftianyi.top/signal}"
PUBLIC_SIGNALING_TOKEN="${OKCLAW_PUBLIC_SIGNALING_TOKEN:-c997a82be3d4e66b2e6d8f0a2e7a89739fcca8cde39bde429f82e198bc7ebe4d}"
TMP_PACKAGE=""
EXTRACT_DIR=""

usage() {
  cat <<USAGE
OK Work Linux 直装脚本

用法:
  curl -fsSL https://okclaw-client.dftianyi.top/install/linux/quick_start.sh | bash
  curl -fsSL https://okclaw-client.dftianyi.top/install/linux/quick_start.sh | bash -s -- --version 1.0.3

参数:
  --version TAG               包版本，默认: ${VERSION}
  --install-dir PATH          安装目录，默认: ${INSTALL_DIR}
  --service-name NAME         systemd 服务名，默认: ${SERVICE_NAME}
  --port PORT                 服务监听端口；未指定时会提示输入，直接回车默认使用 ${SERVICE_PORT}
  --host HOST                 监听地址，默认: ${HOST}
  --webui-ws-url URL          浏览器 WebSocket 地址，默认根据浏览器访问地址自动生成
  --package-base-url URL      包下载目录，默认: ${PACKAGE_BASE_URL}
  --package-url URL           完整包下载地址，优先级高于 package-base-url
  --package-api-base URL      最新版本查询接口地址，默认: ${PACKAGE_API_BASE}
  --software-code CODE        软件编码，默认: ${SOFTWARE_CODE}
  --platform-type TYPE        Linux 平台类型，默认: ${LINUX_PLATFORM_TYPE}
  --no-package-api            不查询版本接口，直接使用 package-base-url 拼接下载地址
  --public-signaling-url URL  公网远程访问信令地址
  --public-signaling-token T  公网远程访问信令 Token
  -h, --help                  显示帮助

包命名:
  okclawpro-server-<version>-linux-x64.tar.gz
  okclawpro-server-<version>-linux-arm64.tar.gz
USAGE
}

log() { printf '\033[1;32m[OKClaw]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[OKClaw]\033[0m %s\n' "$*" >&2; }
err() { printf '\033[1;31m[OKClaw]\033[0m %s\n' "$*" >&2; }

cleanup() {
  [ -n "${TMP_PACKAGE:-}" ] && rm -f "$TMP_PACKAGE"
  [ -n "${EXTRACT_DIR:-}" ] && rm -rf "$EXTRACT_DIR"
}

require_root() {
  if [ "$(id -u)" -ne 0 ]; then
    err "请使用 root 用户执行，或使用 sudo: curl -fsSL ... | sudo bash"
    exit 1
  fi
}

parse_args() {
  while [ "$#" -gt 0 ]; do
    case "$1" in
      --version) VERSION="${2:?missing value}"; shift 2 ;;
      --install-dir) INSTALL_DIR="${2:?missing value}"; shift 2 ;;
      --service-name) SERVICE_NAME="${2:?missing value}"; shift 2 ;;
      --port) PORT="${2:?missing value}"; PORT_WAS_PROVIDED=true; shift 2 ;;
      --host) HOST="${2:?missing value}"; shift 2 ;;
      --webui-ws-url) WEBUI_WS_URL="${2:?missing value}"; shift 2 ;;
      --package-base-url) PACKAGE_BASE_URL="${2:?missing value}"; shift 2 ;;
      --package-url) PACKAGE_URL="${2:?missing value}"; shift 2 ;;
      --package-api-base) PACKAGE_API_BASE="${2:?missing value}"; shift 2 ;;
      --software-code) SOFTWARE_CODE="${2:?missing value}"; shift 2 ;;
      --platform-type) LINUX_PLATFORM_TYPE="${2:?missing value}"; shift 2 ;;
      --no-package-api) PACKAGE_API_ENABLED=false; shift ;;
      --public-signaling-url) PUBLIC_SIGNALING_URL="${2:?missing value}"; shift 2 ;;
      --public-signaling-token) PUBLIC_SIGNALING_TOKEN="${2:?missing value}"; shift 2 ;;
      -h|--help) usage; exit 0 ;;
      *) err "未知参数: $1"; usage; exit 1 ;;
    esac
  done
}

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

is_valid_port() {
  local port="$1"
  [ -n "$port" ] || return 1
  case "$port" in
    *[!0-9]*) return 1 ;;
  esac
  [ "$port" -ge 1 ] && [ "$port" -le 65535 ]
}

is_port_available() {
  local port="$1"

  if command_exists ss; then
    ! ss -H -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "(^|[.:])${port}$"
    return
  fi

  if command_exists netstat; then
    ! netstat -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "(^|[.:])${port}$"
    return
  fi

  if command_exists lsof; then
    ! lsof -iTCP:"${port}" -sTCP:LISTEN -Pn >/dev/null 2>&1
    return
  fi

  return 0
}

find_free_port() {
  local port
  for _ in $(seq 1 200); do
    if command_exists shuf; then
      port="$(shuf -i 10000-59999 -n 1)"
    else
      port="$((10000 + RANDOM % 50000))"
    fi
    if is_port_available "$port"; then
      echo "$port"
      return 0
    fi
  done
  return 1
}

resolve_listen_port() {
  local requested="$1"
  local is_manual="${2:-false}"
  local fallback_port

  if [ -z "$requested" ]; then
    if is_port_available "$SERVICE_PORT"; then
      echo "$SERVICE_PORT"
      return 0
    fi

    fallback_port="$(find_free_port)" || {
      err "默认监听端口 ${SERVICE_PORT} 已被占用，并且未能自动找到可用端口。请使用 --port 指定端口后重试。"
      return 1
    }
    warn "默认监听端口 ${SERVICE_PORT} 已被占用，已自动选择空闲监听端口 ${fallback_port}。"
    echo "$fallback_port"
    return 0
  fi

  if ! is_valid_port "$requested"; then
    err "端口必须是 1-65535 之间的数字，当前输入: ${requested}"
    return 1
  fi

  if ! is_port_available "$requested"; then
    if [ "$is_manual" = true ]; then
      err "端口 ${requested} 已被占用，请重新输入一个未被占用的服务监听端口。"
    else
      err "指定的端口 ${requested} 已被占用，请换一个端口后重试。"
    fi
    return 1
  fi

  echo "$requested"
}

prompt_for_port() {
  local input selected

  if [ ! -t 0 ] && [ ! -r /dev/tty ]; then
    selected="$(resolve_listen_port "" false)"
    PORT="$selected"
    return 0
  fi

  while true; do
    printf '\n[OKClaw] 请输入 OKClaw Web 服务要监听的端口。\n' >/dev/tty
    printf '[OKClaw] 直接回车默认使用监听端口 %s。\n' "$SERVICE_PORT" >/dev/tty
    printf '[OKClaw] 如果默认端口已被占用，脚本会自动选择一个未占用端口。\n' >/dev/tty
    printf '[OKClaw] 服务监听端口: ' >/dev/tty

    if ! IFS= read -r input </dev/tty; then
      input=""
    fi

    if [ -z "$input" ]; then
      selected="$(resolve_listen_port "" false)"
      PORT="$selected"
      return 0
    fi

    if selected="$(resolve_listen_port "$input" true)"; then
      PORT="$selected"
      return 0
    fi
  done
}

select_port() {
  local selected existing_port

  existing_port="$(get_existing_listen_port)"
  if [ "$PORT_WAS_PROVIDED" = true ]; then
    if [ -n "$existing_port" ] && [ "$PORT" = "$existing_port" ]; then
      log "检测到已有安装，继续使用原监听端口: ${existing_port}"
      PORT="$existing_port"
      return 0
    fi

    selected="$(resolve_listen_port "$PORT" false)"
    PORT="$selected"
    return 0
  fi

  if [ -n "$existing_port" ]; then
    log "检测到已有安装，继续使用原监听端口: ${existing_port}"
    PORT="$existing_port"
    return 0
  fi

  prompt_for_port
}

install_prereqs() {
  local missing=""
  for cmd in bash curl tar gzip systemctl; do
    command_exists "$cmd" || missing="$missing $cmd"
  done
  [ -z "$missing" ] || log "缺少依赖:${missing}，尝试自动安装..."

  if command_exists apt-get; then
    apt-get update
    apt-get install -y bash ca-certificates curl tar gzip git nodejs openssh-client ripgrep python3
  elif command_exists dnf; then
    dnf install -y bash ca-certificates curl tar gzip git nodejs openssh-clients ripgrep python3
  elif command_exists yum; then
    yum install -y bash ca-certificates curl tar gzip git nodejs openssh-clients python3
    yum install -y ripgrep || true
  elif command_exists apk; then
    apk add --no-cache bash ca-certificates curl tar gzip git nodejs openssh-client ripgrep python3
  elif [ -n "$missing" ]; then
    err "无法自动安装依赖:${missing}"
    exit 1
  fi

  command_exists systemctl || {
    err "当前系统没有 systemd/systemctl，暂不支持自动安装为后台服务"
    exit 1
  }
}

detect_arch() {
  local machine
  machine="$(uname -m)"
  case "$machine" in
    x86_64|amd64) echo "x64" ;;
    aarch64|arm64) echo "arm64" ;;
    *) err "暂不支持当前架构: ${machine}"; exit 1 ;;
  esac
}

gen_token() {
  if command_exists openssl; then
    openssl rand -hex 32
  else
    tr -dc 'a-f0-9' </dev/urandom | head -c 64
    echo
  fi
}

get_env_value() {
  local key="$1" file="$2"
  [ -f "$file" ] || return 1
  grep -E "^${key}=" "$file" | tail -n1 | cut -d= -f2-
}

get_existing_listen_port() {
  local env_file="$INSTALL_DIR/.env"
  local existing_port
  existing_port="$(get_env_value CRAFT_RPC_PORT "$env_file" 2>/dev/null || true)"

  if is_valid_port "$existing_port"; then
    echo "$existing_port"
  fi
}

pick_ip() {
  hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.' | grep -v '^127\.' | head -n1 || true
}

build_static_package_url() {
  local arch
  arch="$(detect_arch)"
  echo "${PACKAGE_BASE_URL%/}/okclawpro-server-${VERSION}-linux-${arch}.tar.gz"
}

fetch_latest_package_json() {
  local url
  url="${PACKAGE_API_BASE%/}/get-latest-version?softwareCode=${SOFTWARE_CODE}&platformType=${LINUX_PLATFORM_TYPE}"
  curl -fsSL --connect-timeout 10 --max-time 15 --retry 1 -H 'Accept: application/json' "$url"
}

extract_download_url_from_json() {
  local json="$1"
  command_exists python3 || return 1

  printf '%s' "$json" | python3 -c '
import json
import sys

try:
    result = json.load(sys.stdin)
except Exception:
    sys.exit(1)

if not (result.get("success") is True and result.get("code") == 200):
    sys.exit(1)

data = result.get("data")
if not isinstance(data, dict):
    sys.exit(1)

url = str(data.get("downloadUrl") or "").strip()
if not url:
    sys.exit(1)

print(url)
'
}

resolve_package_url() {
  if [ -n "$PACKAGE_URL" ]; then
    echo "$PACKAGE_URL"
    return
  fi

  local json url
  if [ "$PACKAGE_API_ENABLED" = true ]; then
    if json="$(fetch_latest_package_json)"; then
      if url="$(extract_download_url_from_json "$json")" && [ -n "$url" ]; then
        echo "$url"
        return
      fi
      warn "版本接口未返回可用的 Linux 下载地址，改用静态包地址。"
    else
      warn "查询 Linux 安装包版本接口失败，改用静态包地址。"
    fi
  fi

  build_static_package_url
}

build_package_url() {
  resolve_package_url
}

download_package() {
  local url="$1" dest="$2"
  log "下载 Linux 安装包: ${url}"
  if ! curl -fL --connect-timeout 10 --retry 2 -o "$dest" "$url"; then
    cat >&2 <<EOF

未能下载安装包。
请确认包已经上传，或执行时传入:

  --package-url https://example.com/okclawpro-server-${VERSION}-linux-$(detect_arch).tar.gz

当前预期地址:
  ${url}

EOF
    exit 20
  fi
}

write_env() {
  local env_file="$INSTALL_DIR/.env"
  local token password ws_url
  token="$(get_env_value CRAFT_SERVER_TOKEN "$env_file" || true)"
  password="$(get_env_value CRAFT_WEBUI_PASSWORD "$env_file" || true)"
  [ -n "$token" ] || token="$(gen_token)"
  [ -n "$password" ] || password="$(gen_token | cut -c1-24)"

  ws_url="$WEBUI_WS_URL"
  ws_url="$(printf '%s' "$ws_url" | tr -d '\r')"

  cat >"$env_file" <<EOF
CRAFT_SERVER_TOKEN=${token}
CRAFT_WEBUI_PASSWORD=${password}
CRAFT_RPC_HOST=${HOST}
CRAFT_RPC_PORT=${PORT}
CRAFT_DEBUG=false
TZ=Asia/Shanghai
OKCLAW_RUNTIME_KIND=linux
OKCLAW_INSTALL_DIR=${INSTALL_DIR}
OKCLAW_SERVICE_NAME=${SERVICE_NAME}
OKCLAW_PUBLIC_SIGNALING_URL=${PUBLIC_SIGNALING_URL}
OKCLAW_PUBLIC_SIGNALING_TOKEN=${PUBLIC_SIGNALING_TOKEN}
EOF
  if [ -n "$ws_url" ]; then
    printf 'CRAFT_WEBUI_WS_URL=%s\n' "$ws_url" >>"$env_file"
  fi
  chmod 600 "$env_file"
}

install_systemd() {
  local service_file="/etc/systemd/system/${SERVICE_NAME}.service"
  cat >"$service_file" <<EOF
[Unit]
Description=OK Work Server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=${INSTALL_DIR}
EnvironmentFile=${INSTALL_DIR}/.env
Environment=CRAFT_IS_PACKAGED=false
Environment=CRAFT_APP_ROOT=${INSTALL_DIR}
Environment=CRAFT_BUNDLED_ASSETS_ROOT=${INSTALL_DIR}/apps/electron
Environment=CRAFT_RESOURCES_PATH=${INSTALL_DIR}/apps/electron/resources
Environment=CRAFT_SESSION_SERVER=${INSTALL_DIR}/packages/session-mcp-server/dist/index.js
Environment=CRAFT_BRIDGE_SERVER=${INSTALL_DIR}/apps/electron/resources/bridge-mcp-server/index.js
Environment=CRAFT_PI_SERVER=${INSTALL_DIR}/packages/pi-agent-server/dist/index.js
Environment=CRAFT_UV=${INSTALL_DIR}/apps/electron/resources/bin/uv
Environment=CRAFT_SCRIPTS=${INSTALL_DIR}/apps/electron/resources/scripts
Environment=CRAFT_WEBUI_DIR=${INSTALL_DIR}/apps/webui/dist
Environment=PATH=${INSTALL_DIR}/apps/electron/resources/bin:${INSTALL_DIR}/vendor/bun:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=${INSTALL_DIR}/bin/okclawpro-server --allow-insecure-bind
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  systemctl daemon-reload
  systemctl enable "$SERVICE_NAME" >/dev/null
  systemctl restart "$SERVICE_NAME"
}

wait_health() {
  local url="http://127.0.0.1:${PORT}/health"
  for _ in $(seq 1 45); do
    if curl -fsS "$url" >/dev/null 2>&1; then
      return 0
    fi
    sleep 2
  done
  return 1
}

print_result() {
  local ip public_url token password ws_url
  ip="$(pick_ip)"
  [ -n "$ip" ] || ip="127.0.0.1"
  public_url="请使用服务器实际公网 IP 或域名加端口访问，例如: http://<公网IP>:${PORT}"
  token="$(get_env_value CRAFT_SERVER_TOKEN "$INSTALL_DIR/.env")"
  password="$(get_env_value CRAFT_WEBUI_PASSWORD "$INSTALL_DIR/.env")"
  ws_url="$(get_env_value CRAFT_WEBUI_WS_URL "$INSTALL_DIR/.env" || true)"
  [ -n "$ws_url" ] || ws_url="自动根据浏览器访问地址生成（会使用你打开页面时的域名/IP 和端口）"

  cat <<EOF

============================================================
OK Work Linux 直装版已安装并启动
============================================================

内网访问地址:
  http://${ip}:${PORT}
  http://127.0.0.1:${PORT}

公网访问地址:
  ${public_url}

WebSocket 地址:
  ${ws_url}

Web 访问 Token:
  ${token}

Web 登录密码(如界面要求):
  ${password}

安装目录:
  ${INSTALL_DIR}

服务管理:
  systemctl status ${SERVICE_NAME}
  systemctl restart ${SERVICE_NAME}
  journalctl -u ${SERVICE_NAME} -f

EOF
}

main() {
  parse_args "$@"
  require_root
  install_prereqs
  select_port

  local url
  url="$(resolve_package_url)"
  TMP_PACKAGE="$(mktemp -t okclawpro-server.XXXXXX.tar.gz)"
  EXTRACT_DIR="$(mktemp -d -t okclawpro-server.XXXXXX)"
  trap cleanup EXIT

  download_package "$url" "$TMP_PACKAGE"
  log "解压安装包到临时目录"
  tar -xzf "$TMP_PACKAGE" -C "$EXTRACT_DIR"

  log "安装到 ${INSTALL_DIR}"
  mkdir -p "$INSTALL_DIR"
  if [ -f "$INSTALL_DIR/.env" ]; then
    cp "$INSTALL_DIR/.env" "$EXTRACT_DIR/.env.keep"
  fi
  find "$INSTALL_DIR" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
  cp -a "$EXTRACT_DIR/." "$INSTALL_DIR/"
  if [ -f "$EXTRACT_DIR/.env.keep" ]; then
    cp "$EXTRACT_DIR/.env.keep" "$INSTALL_DIR/.env"
    rm -f "$INSTALL_DIR/.env.keep"
  fi

  chmod +x "$INSTALL_DIR/bin/okclawpro-server" "$INSTALL_DIR/start.sh" 2>/dev/null || true
  [ -f "$INSTALL_DIR/vendor/bun/bun" ] && chmod +x "$INSTALL_DIR/vendor/bun/bun"
  [ -f "$INSTALL_DIR/apps/electron/resources/bin/uv" ] && chmod +x "$INSTALL_DIR/apps/electron/resources/bin/uv"
  find "$INSTALL_DIR/apps/electron/resources/bin" -maxdepth 2 -type f -exec chmod +x {} \; 2>/dev/null || true

  write_env
  install_systemd
  if wait_health; then
    log "服务健康检查通过"
  else
    warn "服务健康检查未在预期时间内通过，请执行 journalctl -u ${SERVICE_NAME} -f 查看日志"
  fi
  print_result
}

if [ "${OKCLAW_QUICK_START_SOURCE_ONLY:-false}" != true ]; then
  main "$@"
fi
