#!/bin/bash
# Aaden 收银机装机：全新的 Ubuntu（或 WSL）→ 可以交付的收银机。
#   aaden-cli install <设备号>        （或在 AadenDeploy 目录下 ./aadensetup.sh <设备号>）
#
# - 设备号最先问并查云端（checkDeviceId.sh），撞号在装任何东西之前拦下来
# - 每一步记下退出码和耗时；关键步骤失败就停下；最后做一遍自检，连同每一步的结果作为「出厂安装」事件上报云端
# - 全部输出另存一份到 /var/log/aaden-setup.log
# - 可以重跑：数据库已经初始化过就跳过，配置、主机名、/etc/hosts、定时任务都是覆盖写，不会重复追加
export PATH="/usr/local/bin:$PATH"
DIR="$(cd "$(dirname "$0")" && pwd)"
PW='Innerken322.'
LOG=/var/log/aaden-setup.log
WEB=/var/www/html
BACKEND_SRC=/etc/aaden/updateFiles/backendFiles

# 和 aaden-cli 其他地方一样用 -S 从 stdin 喂密码。不能再往 sudoS 的命令里重定向 stdin（密码会被当成内容），
# 需要重定向时写成 sudoS sh -c '… < 文件'
sudoS() { echo "$PW" | sudo -S -p '' "$@"; }
is_wsl() { grep -qi microsoft /proc/version 2>/dev/null; }

banner() {
  echo ""
  echo "#######################################################################"
  echo "# $*"
  echo "#######################################################################"
}

banner "Aaden 收银机装机"

# ---------------------------------------------------------------- 设备号（最先问，先查撞号）
DEVICE_ID="$1"
if [ -z "$DEVICE_ID" ]; then
  read -r -p '设备号: ' DEVICE_ID
fi
# 测试门店号段先拦（不用联网），再查云端撞号
source "$DIR/farmRange.sh" && farm_range_guard "$DEVICE_ID" || exit 1
bash "$DIR/checkDeviceId.sh" "$DEVICE_ID" || exit 1
DEVICE_ID=$((10#$DEVICE_ID))
export AADEN_SKIP_DEVICE_CHECK=1 # 后面 set-device-id 不再问第二遍

# sudo 凭据先缓存上并一直续着：装机要十几分钟，中途调的 aaden-cli 子命令里还有裸 sudo
sudoS -v 2>/dev/null || sudo -v || exit 1
(while kill -0 $$ 2>/dev/null; do sudoS -v 2>/dev/null; sleep 50; done) &
KEEPALIVE=$!
trap 'kill $KEEPALIVE 2>/dev/null' EXIT

sudoS touch "$LOG" && sudoS chmod 666 "$LOG"
exec > >(tee -a "$LOG") 2>&1
echo "$(date '+%F %T') 开始装机，设备号 $DEVICE_ID"

# ---------------------------------------------------------------- 步骤记录与上报
STEPS=""
step() { # step <名字> <命令…>：输出照常打到屏幕，记下退出码和耗时
  local name=$1 start=$SECONDS code
  shift
  banner "$name"
  "$@"
  code=$?
  STEPS="$STEPS $name=$code/$((SECONDS - start))s"
  [ "$code" = 0 ] || echo "✗ $name 失败（退出码 $code），详细输出见 $LOG"
  return "$code"
}

report() { # report <result> <附加内容>：「出厂安装」事件，在云端设备事件里能看到每一步和自检结果
  local content="出厂安装 [${STEPS# }]$2 result:$1"
  python3 - "$DEVICE_ID" "$content" "$(aaden-cli --version 2>/dev/null | tail -n 1)" <<'PY' || echo "（上报云端失败）"
import json, sys, urllib.request
device_id, content, version = sys.argv[1:4]
body = json.dumps({"deviceId": device_id, "content": content, "type": "系统生成", "operator": "aaden-cli v" + version}).encode()
urllib.request.urlopen(urllib.request.Request("https://cloud-v2.aaden.io/deviceLog/createEvent", body, {"Content-Type": "application/json"}), timeout=10)
PY
}

must() { # 关键步骤：失败就上报并停下，后面的步骤都依赖它
  step "$@" && return 0
  report 1 " 中止于 $1"
  banner "装机中止：$1 失败。修好之后重新运行同一条命令即可，已经完成的部分会跳过或覆盖。日志：$LOG"
  exit 1
}

# ---------------------------------------------------------------- 1. 系统软件
install_packages() {
  local apt="env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold"
  # 上次装到一半断掉（断电、断网、包脚本失败）留下的半配置状态先收拾掉，否则后面的 apt 全部报错
  sudoS env DEBIAN_FRONTEND=noninteractive dpkg --configure -a
  command -v add-apt-repository >/dev/null || sudoS $apt install software-properties-common || return 1
  # Ubuntu 22.04 起官方源没有 PHP 7.4
  sudoS add-apt-repository -y ppa:ondrej/php || return 1
  sudoS apt-get update || return 1
  sudoS $apt install \
    apache2 mysql-server \
    php7.4 php7.4-common php7.4-cli libapache2-mod-php7.4 \
    php7.4-xml php7.4-curl php7.4-mysql php7.4-gd php7.4-zip php7.4-intl php7.4-mbstring \
    default-jdk openssh-server \
    curl wget unzip axel expect vim net-tools ttyload ca-certificates apt-transport-https \
    python3-requests || return 1
  # 装了别的 PHP 版本时，命令行默认的 php 也指向 7.4
  sudoS update-alternatives --set php /usr/bin/php7.4
  sudoS update-alternatives --set phar /usr/bin/phar7.4 2>/dev/null
  sudoS update-alternatives --set phar.phar /usr/bin/phar.phar7.4 2>/dev/null
  sudoS systemctl restart apache2 2>/dev/null || sudoS service apache2 restart
}
must apt install_packages

# ---------------------------------------------------------------- 2. 后端代码
download_backend() {
  bash "$DIR/files/getFile.sh" && [ -f "$BACKEND_SRC/package.json" ]
}
must download download_backend

# ---------------------------------------------------------------- 3. 数据库
setup_database() {
  # 先确保 MySQL 在跑（没有 systemd 的 WSL 不会自动起），否则下面的判断会把已有的库当成没有
  sudoS service mysql start 2>/dev/null
  # 没有 systemd 时 socket 目录可能是 700，aaden 跑数据库迁移会连不上（SQLSTATE 2002 Permission denied）
  sudoS chmod 755 /var/run/mysqld 2>/dev/null
  if sudoS mysql -uroot -e 'USE aaden' 2>/dev/null || sudoS test -d /var/lib/mysql/aaden; then
    echo "数据库 aaden 已存在，跳过初始化（重跑装机不会覆盖门店数据）"
    return 0
  fi
  sudoS bash "$DIR/scureSql.sh" "$PW"
  sudoS sh -c "mysql -uroot < '$BACKEND_SRC/new.sql'" && sudoS mysql -uroot -e 'USE aaden'
}
must db setup_database

# ---------------------------------------------------------------- 4. 部署后端
deploy_backend() {
  # cp 而不是 mv：重跑时目标目录已存在，mv 会失败
  sudoS cp -a "$BACKEND_SRC/." "$WEB/" || return 1
  sudoS rm -rf "$BACKEND_SRC"
  sudoS chown -hR www-data:www-data "$WEB"
  sudoS chmod -R 777 "$WEB/Resource"
  sudoS chmod +x "$WEB/crontab.sh" "$WEB/PHP/vendor/bin/phinx" "$WEB/PHP/vendor/robmorgan/phinx/bin/phinx"
  sudoS systemctl restart apache2 2>/dev/null || sudoS service apache2 restart
  sleep 2
  curl -fsS -m 10 http://localhost/package.json | grep '"version"'
}
must deploy deploy_backend

# ---------------------------------------------------------------- 5. 设备号：配置、主机名、ngrok、上报
must device aaden-cli set-device-id "$DEVICE_ID"

# ---------------------------------------------------------------- 6. 数据库迁移
must migrate sh -c "cd '$WEB/PHP' && vendor/bin/phinx migrate"

# ---------------------------------------------------------------- 7. 系统配置（失败不中止，最后的自检会标出来）
add_groups() {
  # 打印机（lp）、读日志（adm）、aaden 能 sudo；www-data 要能访问 USB 打印机
  sudoS usermod -a -G lp,root,adm,sudo aaden && sudoS usermod -a -G lp,root www-data
}
step groups add_groups
step patch aaden-cli patch
# 日结物化守护进程：每日数据库备份和 DSFinV-K 日结物化都靠它
step gdpdu aaden-cli auto-write-gdpdu
# aaden 和 root 的定时任务整份覆盖写（do-log 上报、每日自动更新等）
step cron aaden-cli fix-cronjob
# 升级 aaden-cli、跑补丁、后端 light-update、同步 AppCenter 前端
step upgrade aaden-cli upgrade

if ! is_wsl; then
  install_teamviewer() {
    local deb
    deb=$(mktemp --suffix=.deb)
    wget -q -O "$deb" https://download.teamviewer.com/download/linux/teamviewer_amd64.deb \
      && sudoS env DEBIAN_FRONTEND=noninteractive apt-get -y install "$deb"
    local code=$?
    rm -f "$deb"
    wget -q -O "$HOME/background.png" https://aaden.online/AadenOS/background.png
    return $code
  }
  step teamviewer install_teamviewer
fi

# ---------------------------------------------------------------- 8. 自检
banner "自检"
CHECKS=""
BAD=""
check() { # check <key> <说明> <命令…>
  local key=$1 label=$2
  shift 2
  if "$@" >/dev/null 2>&1; then
    CHECKS="$CHECKS $key=ok"
    echo "  ✓ $label"
  else
    CHECKS="$CHECKS $key=FAIL"
    BAD="$BAD $label"
    echo "  ✗ $label"
  fi
}
NGROK_HOST="ik$(printf '%04d' "$DEVICE_ID").ngrok.aaden.io"

check_cloud() {
  aaden-cli do-log >/dev/null 2>&1
  curl -fsS -m 15 "https://cloud-v2.aaden.io/deviceLog/install-check/$DEVICE_ID" \
    | python3 -c 'import json,sys; m = json.load(sys.stdin).get("lastReportMinutesAgo"); sys.exit(0 if m is not None and m <= 10 else 1)'
}
check_ngrok() { # 隧道刚起来时要等几秒
  local i
  for i in 1 2 3 4 5 6; do
    curl -fsS -m 10 "https://$NGROK_HOST/package.json" | grep -q '"version"' && return 0
    sleep 10
  done
  return 1
}
check_gdpdu() {
  systemctl is-active --quiet aaden-gdpdu.service 2>/dev/null \
    || pgrep -f 'autoWriteGdpdu\.php' >/dev/null \
    || [ -f /etc/cron.d/aaden-gdpdu ]
}
check_cron() {
  crontab -l 2>/dev/null | grep -q 'do-log' && sudoS crontab -l -u root 2>/dev/null | grep -q 'auto-upgrade'
}

check_id() {
  [ "$(awk -F= '/^\[/ { s = $0 } s ~ /^\[Device\]/ && $1 ~ /^[ \t]*DeviceId[ \t]*$/ { gsub(/[ \t"\r]/, "", $2); print $2; exit }' "$WEB/Resource/config/config.ini")" = "$DEVICE_ID" ]
}

check backend "后端在跑（http://localhost/package.json）" sh -c 'curl -fsS -m 10 http://localhost/package.json | grep -q version'
check id "设备号写入配置（$DEVICE_ID）" check_id
check cloud "云端收到本机上报（ERP 门店列表「机器」列显示就绪）" check_cloud
check ngrok "远程访问 https://$NGROK_HOST" check_ngrok
check gdpdu "日结守护进程 aaden-gdpdu（没有它就没有每日备份）" check_gdpdu
check cron "定时任务（上报、每日自动更新）" check_cron

# Windows 门店：Smart App Control 开着时，没签名的桌面收银安装包会被 Windows 拦住，以后远程升级装不上（POS-148）。
# 只提示，不算装机失败；关掉要店主同意（关了不能再打开）
if is_wsl; then
  SAC=$(timeout 15 /mnt/c/Windows/System32/reg.exe query 'HKLM\SYSTEM\CurrentControlSet\Control\CI\Policy' /v VerifiedAndReputablePolicyState 2>/dev/null \
    | awk '/VerifiedAndReputablePolicyState/ { print $3 }' | tr -d '\r')
  case "$SAC" in
    0x0) CHECKS="$CHECKS sac=off"; echo "  ✓ Smart App Control 已关闭" ;;
    0x1) CHECKS="$CHECKS sac=on"
         echo "  ! Smart App Control 开着：以后桌面收银的远程升级会被 Windows 拦住。"
         echo "    和店主确认后，在「Windows 安全中心 → 应用和浏览器控制 → 智能应用控制」里关掉（关掉后不能再打开）" ;;
    0x2) CHECKS="$CHECKS sac=evaluation"
         echo "  ! Smart App Control 在评估模式：Windows 之后可能自动打开它，届时远程升级会被拦。建议和店主确认后直接关掉" ;;
  esac
fi

FAILED_STEPS=$(echo "$STEPS" | tr ' ' '\n' | grep -v '=0/' | cut -d= -f1 | tr '\n' ' ')
if [ -z "$BAD" ] && [ -z "${FAILED_STEPS// /}" ]; then
  report 0 " [自检${CHECKS}]"
  banner "装机完成，设备号 $DEVICE_ID，可以交付"
  exit 0
fi
report 1 " [自检${CHECKS}]"
banner "装机跑完了，但有问题要处理"
[ -n "${FAILED_STEPS// /}" ] && echo "失败的步骤：$FAILED_STEPS"
[ -n "$BAD" ] && echo "没通过的自检：$BAD"
echo "处理完可以重新运行同一条命令。日志：$LOG"
exit 1
