#!/usr/bin/env bash
#
# 中学成绩档案系统 — 一键部署
# 架构：主程序 + OCR(GPU/screen 同机) | Ollama(其他电脑局域网)
# 版权所有 (c) 马建军  微信: dekun03  手机: 18364911125
#
set -euo pipefail

REPO_URL="${REPO_URL:-https://git.bz121.com/dekun/secondary-school-grade-archive.git}"
INSTALL_DIR="${INSTALL_DIR:-/opt/secondary-school-grade-archive}"
WEB_PORT="${WEB_PORT:-23566}"
OCR_PORT="${OCR_PORT:-23567}"
BRANCH="${BRANCH:-main}"
PIP_MIRROR="${PIP_MIRROR:-https://pypi.tuna.tsinghua.edu.cn/simple}"
# Ollama 在其他电脑时安装前指定，例如: OLLAMA_BASE_URL=http://192.168.8.100:11434
OLLAMA_BASE_URL="${OLLAMA_BASE_URL:-}"
OLLAMA_MODEL="${OLLAMA_MODEL:-qwen2.5:7b}"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log_info()  { echo -e "${GREEN}[INFO]${NC} $*"; }
log_warn()  { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }

# shellcheck source=proxy.sh
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/proxy.sh"
# shellcheck source=ocr-common.sh
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/ocr-common.sh"

require_root() {
  if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
    log_error "请使用 root 用户运行: sudo bash deploy/install.sh"
    exit 1
  fi
}

check_os() {
  if [[ ! -f /etc/os-release ]]; then
    log_error "无法识别操作系统"
    exit 1
  fi
  # shellcheck source=/dev/null
  source /etc/os-release
  log_info "检测到系统: ${NAME:-Unknown} ${VERSION_ID:-}"
}

check_port() {
  for p in "${WEB_PORT}" "${OCR_PORT}"; do
    if command -v ss &>/dev/null && ss -tln | grep -q ":${p} "; then
      if [[ "${p}" == "${OCR_PORT}" ]] && ocr_screen_running 2>/dev/null; then
        log_warn "端口 ${OCR_PORT} 已被占用（可能已有 OCR Worker）"
      elif [[ "${p}" == "${WEB_PORT}" ]]; then
        log_error "端口 ${WEB_PORT} 已被占用"
        exit 1
      fi
    fi
  done
  log_info "Web 端口 ${WEB_PORT} / OCR 端口 ${OCR_PORT} 检查完成"
}

prompt_ollama_url() {
  if [[ -n "${OLLAMA_BASE_URL}" ]]; then
    log_info "Ollama 地址: ${OLLAMA_BASE_URL}"
    return
  fi
  if [[ -t 0 ]]; then
    echo ""
    echo "Ollama 部署在【其他电脑】上，请填写局域网地址。"
    read -rp "Ollama 地址 [http://192.168.8.100:11434]: " input
    OLLAMA_BASE_URL="${input:-http://192.168.8.100:11434}"
  else
    OLLAMA_BASE_URL="http://127.0.0.1:11434"
    log_warn "未设置 OLLAMA_BASE_URL，默认 ${OLLAMA_BASE_URL}（可在 .env 或系统设置中修改）"
  fi
  log_info "Ollama 地址: ${OLLAMA_BASE_URL}"
}

install_base_packages() {
  log_info "安装系统依赖…"
  apt-get update -qq
  DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
    git curl ca-certificates openssl screen \
    python3 python3-venv python3-pip python3-dev \
    build-essential libpq-dev \
    postgresql postgresql-contrib \
    libgl1 libglx-mesa0 libgbm1 \
    libgomp1 libglib2.0-0 libsm6 libxrender1 libxext6 libxcb1 libfontconfig1
}

clone_or_update_repo() {
  if [[ -d "${INSTALL_DIR}/.git" ]]; then
    log_info "更新代码: ${INSTALL_DIR}"
    git -C "${INSTALL_DIR}" fetch origin
    git -C "${INSTALL_DIR}" checkout "${BRANCH}" 2>/dev/null || true
    git -C "${INSTALL_DIR}" pull origin "${BRANCH}"
  else
    log_info "克隆仓库到 ${INSTALL_DIR}"
    mkdir -p "$(dirname "${INSTALL_DIR}")"
    git clone --branch "${BRANCH}" "${REPO_URL}" "${INSTALL_DIR}" || git clone "${REPO_URL}" "${INSTALL_DIR}"
  fi
  find "${INSTALL_DIR}" -name "*.sh" -exec sed -i 's/\r$//' {} +
  chmod +x "${INSTALL_DIR}/deploy/start.sh" \
    "${INSTALL_DIR}/deploy/ocr-screen.sh" \
    "${INSTALL_DIR}/deploy/ocr-worker/"*.sh 2>/dev/null || true
}

verify_frontend_dist() {
  if [[ ! -f "${INSTALL_DIR}/frontend/dist/index.html" ]]; then
    log_error "未找到 frontend/dist/index.html"
    log_error "仓库应已包含预构建前端；若缺失请在开发机 npm run build 后推送"
    exit 1
  fi
  log_info "前端静态资源已就绪"
}

env_set_or_append() {
  local key="$1" val="$2" file="${INSTALL_DIR}/.env"
  if grep -q "^${key}=" "${file}" 2>/dev/null; then
    sed -i "s|^${key}=.*|${key}=${val}|" "${file}"
  else
    echo "${key}=${val}" >> "${file}"
  fi
}

generate_env() {
  local env_file="${INSTALL_DIR}/.env"
  local server_ip
  server_ip=$(hostname -I 2>/dev/null | awk '{print $1}')
  server_ip="${server_ip:-127.0.0.1}"

  prompt_ollama_url

  if [[ ! -f "${env_file}" ]]; then
    local secret pg_pass pg_user
    secret=$(openssl rand -hex 32)
    pg_pass=$(openssl rand -hex 16)
    pg_user="gradeapp"

    cat > "${env_file}" <<EOF
# generated by deploy/install.sh — $(date -Iseconds)
WEB_PORT=${WEB_PORT}
FRONTEND_DIST=${INSTALL_DIR}/frontend/dist
SECRET_KEY=${secret}
POSTGRES_USER=${pg_user}
POSTGRES_PASSWORD=${pg_pass}
POSTGRES_DB=student_archive
DATABASE_URL=postgresql://${pg_user}:${pg_pass}@127.0.0.1:5432/student_archive
UPLOAD_DIR=${INSTALL_DIR}/uploads
BACKUP_DIR=/root/grade-archive-backups
BACKUP_RETENTION_DAYS=30
AUTO_BACKUP_INTERVAL_HOURS=24
CORS_ORIGINS=http://${server_ip}:${WEB_PORT},http://127.0.0.1:${WEB_PORT},http://localhost:${WEB_PORT}
# OCR 同机 GPU Worker（screen 常驻）
OCR_SERVICE_URL=http://127.0.0.1:${OCR_PORT}
OCR_PORT=${OCR_PORT}
# Ollama 在其他电脑（局域网）
OLLAMA_BASE_URL=${OLLAMA_BASE_URL}
OLLAMA_MODEL=${OLLAMA_MODEL}
FLUCTUATION_THRESHOLD=0.08
ADMIN_DEFAULT_USERNAME=admin
ADMIN_DEFAULT_PASSWORD=admin123
EOF
    chmod 600 "${env_file}"
    log_info ".env 已生成（默认 admin / admin123）"
  else
    log_info "更新 .env 中的 OCR / Ollama 配置…"
    env_set_or_append "OCR_SERVICE_URL" "http://127.0.0.1:${OCR_PORT}"
    env_set_or_append "OCR_PORT" "${OCR_PORT}"
    env_set_or_append "OLLAMA_BASE_URL" "${OLLAMA_BASE_URL}"
    env_set_or_append "OLLAMA_MODEL" "${OLLAMA_MODEL}"
  fi
}

setup_postgresql() {
  # shellcheck disable=SC1090
  source "${INSTALL_DIR}/.env"
  log_info "配置 PostgreSQL…"
  systemctl enable postgresql
  systemctl start postgresql

  if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${POSTGRES_USER}'" | grep -q 1; then
    sudo -u postgres psql -c "CREATE USER ${POSTGRES_USER} WITH PASSWORD '${POSTGRES_PASSWORD}';"
  else
    sudo -u postgres psql -c "ALTER USER ${POSTGRES_USER} WITH PASSWORD '${POSTGRES_PASSWORD}';"
  fi

  if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${POSTGRES_DB}'" | grep -q 1; then
    sudo -u postgres psql -c "CREATE DATABASE ${POSTGRES_DB} OWNER ${POSTGRES_USER};"
  fi
  sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_DB} TO ${POSTGRES_USER};"
}

setup_backend() {
  log_info "安装主程序 Python 依赖…"
  cd "${INSTALL_DIR}/backend"
  if [[ ! -d venv ]]; then
    python3 -m venv venv
  fi
  # shellcheck disable=SC1091
  source venv/bin/activate
  pip install --upgrade pip --progress-bar on -i "${PIP_MIRROR}"
  pip install -r requirements.txt --progress-bar on -i "${PIP_MIRROR}"
  deactivate
}

setup_ocr_gpu() {
  if command -v nvidia-smi >/dev/null; then
    log_info "检测到 NVIDIA GPU，OCR 将常驻显存 (screen)"
    nvidia-smi --query-gpu=name,memory.total --format=csv,noheader 2>/dev/null || true
  else
    log_warn "未检测到 NVIDIA GPU，OCR 将使用 CPU（较慢）"
  fi
  if [[ -x "${INSTALL_DIR}/deploy/install-ocr-deps.sh" ]]; then
    bash "${INSTALL_DIR}/deploy/install-ocr-deps.sh" || log_warn "OCR 系统库安装跳过"
  fi
  install_ocr_worker
  start_ocr_screen
  wait_ocr_healthy 30 || log_warn "OCR 后台加载中，继续安装主程序…"
}

stop_legacy_pm2() {
  if command -v pm2 &>/dev/null; then
    pm2 delete grade-api grade-web 2>/dev/null || true
    pm2 save 2>/dev/null || true
    log_info "已停止旧版 PM2 进程"
  fi
}

setup_systemd() {
  log_info "配置 systemd 服务 grade-archive（主程序）…"
  sed "s|/opt/secondary-school-grade-archive|${INSTALL_DIR}|g" \
    "${INSTALL_DIR}/deploy/grade-archive.service" > /etc/systemd/system/grade-archive.service
  systemctl daemon-reload
  systemctl enable grade-archive
}

start_service() {
  log_info "启动主程序…"
  cd "${INSTALL_DIR}"
  mkdir -p uploads backups /root/grade-archive-backups
  chmod +x deploy/backup.sh deploy/restore.sh 2>/dev/null || true
  systemctl restart grade-archive
}

setup_backup_cron() {
  log_info "配置每日自动备份（/root/grade-archive-backups）…"
  cat > /etc/cron.d/grade-archive-backup <<EOF
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 3 * * * root INSTALL_DIR=${INSTALL_DIR} BACKUP_DIR=/root/grade-archive-backups bash ${INSTALL_DIR}/deploy/backup.sh >> /var/log/grade-archive-backup.log 2>&1
EOF
  chmod 644 /etc/cron.d/grade-archive-backup
}

wait_healthy() {
  local i
  log_info "等待主程序就绪（最多 2 分钟）…"
  for i in $(seq 1 40); do
    if curl -sf "http://127.0.0.1:${WEB_PORT}/api/health" >/dev/null; then
      log_info "主程序健康检查通过"
      return 0
    fi
    if (( i % 5 == 0 )); then
      echo -ne "\r${YELLOW}[INFO]${NC} 等待主程序… ${i}/40"
    fi
    sleep 3
  done
  echo ""
  log_warn "主程序健康检查超时: journalctl -u grade-archive -f"
}

print_summary() {
  # shellcheck disable=SC1090
  source "${INSTALL_DIR}/.env"
  local ip
  ip=$(hostname -I 2>/dev/null | awk '{print $1}')
  ip="${ip:-127.0.0.1}"
  echo ""
  echo "=========================================="
  echo "  中学成绩档案 — 一键部署完成"
  echo "  版权所有 (c) 马建军"
  echo "=========================================="
  echo "  访问:     http://${ip}:${WEB_PORT}"
  echo "  管理员:   admin / admin123（请立即修改）"
  echo ""
  echo "  【同机 OCR — GPU 常驻 screen】"
  echo "  OCR 地址: http://127.0.0.1:${OCR_PORT}"
  echo "  状态:     bash ${INSTALL_DIR}/deploy/ocr-screen.sh status"
  echo "  进入终端: screen -r ocr-worker  (Ctrl+A D 退出)"
  echo "  重启 OCR: bash ${INSTALL_DIR}/deploy/ocr-screen.sh restart"
  echo ""
  echo "  【Ollama — 其他电脑】"
  echo "  地址:     ${OLLAMA_BASE_URL}"
  echo "  模型:     ${OLLAMA_MODEL}"
  echo "  可在「系统设置 → AI 模型」修改"
  echo ""
  echo "  主程序:   systemctl status grade-archive"
  echo "  更新:     sudo bash ${INSTALL_DIR}/deploy/update.sh"
  echo "  备份说明: docs/BACKUP.md"
  echo "  卸载:     sudo bash ${INSTALL_DIR}/deploy/uninstall.sh"
  echo "  微信 dekun03  手机 18364911125"
  echo "=========================================="
}

main() {
  log_info "一键部署开始（主程序 + OCR/GPU/screen | Ollama 外置）"
  require_root
  setup_deploy_proxy
  check_os
  clone_or_update_repo
  verify_frontend_dist
  check_port
  install_base_packages
  generate_env
  setup_postgresql
  setup_backend
  setup_ocr_gpu
  stop_legacy_pm2
  setup_systemd
  setup_backup_cron
  start_service
  wait_healthy
  print_summary
}

main "$@"
