#!/bin/bash
# ============================================================
# BigStream3.0 大屏系统 通用一键安装/更新脚本
# （仿照 website/install.sh v1.3，适配大屏 bigstream 发布包）
#
# 用法:
#   # 离线安装（推荐：本地 tarball，不联网）
#   sudo bash bigStream3.0_install.sh -f bigstream-v3.0-0824-0113.tar.gz
#   sudo bash bigStream3.0_install.sh -f ./bigstream-v3.0-0824-0113.tar.gz -p 8030 -d /opt/bigstream_8030
#
#   # 在线安装（需先把 release 包发布到 HTTP 源，默认源见 -u）
#   sudo bash bigStream3.0_install.sh -u https://ai.seek.ninglon.com/release
#   sudo bash bigStream3.0_install.sh -u https://ai.seek.ninglon.com/release -r v3.1-0825-0115
#
#   # 试运行
#   sudo bash bigStream3.0_install.sh -f bigstream-v3.0-0824-0113.tar.gz -n
# ============================================================

set -euo pipefail

# ===== 脚本版本 =====
SCRIPT_VERSION="bigStream3.0_install.sh v1.0"

# ===== 默认配置 =====
DEFAULT_INSTALL_DIR="/opt/bigstream"
DEFAULT_PORT=8030
PKG_PREFIX="bigstream"
SUPERVISOR_PROGRAM="bigstream"

# ===== Release 源（在线模式用；离线模式 -f 不访问网络） =====
# 与 v8.0.4 共用 website/release 发布目录（nginx /release/ → /www/git_code/teachSeek/website/release/）
# 大屏版本指针独立用 bigstream-LATEST.md（与 v8.0.4 的 LATEST.md 互不干扰）
RELEASE_URL="https://ai.seek.ninglon.com/release"

# ===== 颜色 =====
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# ===== 参数变量 =====
RELEASE_VERSION=""
VERSION_SPECIFIED=0
PORT=""
INSTALL_DIR=""
KILL_PORT=0
FORCE_CONFIG=0
DRY_RUN=0
MODE="supervisor"
LOCAL_FILE=""  # 离线安装：本地 tarball 路径

# ===== 宝塔环境变量 =====
BT_ENV=0
BT_PROFILE_DIR=""
BT_CONFIG_JSON=""
BT_STATUS_TXT=""
BT_LOG_DIR=""
BT_SUPCTL=""
BT_SUP_CONF=""

# ===== 日志函数 =====
log()  { echo -e "$(date '+%Y-%m-%d %H:%M:%S') ${BLUE}[INFO]${NC} $*"; }
warn() { echo -e "$(date '+%Y-%m-%d %H:%M:%S') ${YELLOW}[WARN]${NC} $*"; }
err()  { echo -e "$(date '+%Y-%m-%d %H:%M:%S') ${RED}[ERROR]${NC} $*"; }
ok()   { echo -e "$(date '+%Y-%m-%d %H:%M:%S') ${GREEN}[OK]${NC} $*"; }

# ===== 帮助 =====
usage() {
    cat << 'USAGE'
BigStream3.0 大屏系统 通用一键安装/更新

用法:
  bigStream3.0_install.sh [OPTIONS]

选项:
  -f, --file PATH      离线安装：指定本地 tarball 路径（跳过在线下载，推荐）
  -r, --release VER    指定安装版本号（在线模式，默认: 从 bigstream-LATEST.md 获取）
  -u, --url URL        在线模式的 Release 源地址
                       (默认: https://ai.seek.ninglon.com/release，与 v8.0.4 共用发布目录)
  -p, --port PORT      指定服务端口 (默认: 从 .env 读 BACKEND_PORT，再默认 8030)
  -d, --dir DIR        指定安装目录 (默认: /opt/bigstream)
  -k, --kill-port      强制关闭占用端口的进程 (默认不启用)
  -F, --force-config   强制覆盖 conf 目录（摄像头配对数据，保留 .env 不动）
  -m, --mode MODE      部署模式: supervisor (默认，宝塔环境自动用宝塔 Supervisor) / baota / nohup
  -n, --dry-run        试运行，只打印不执行
  -V, --version        显示脚本版本号
  -h, --help           显示帮助

示例:
  # 离线安装（服务器本机 tarball）
  sudo bash bigStream3.0_install.sh -f /root/bigstream-v3.1-0825-0115.tar.gz

  # 离线安装 + 指定端口和目录
  sudo bash bigStream3.0_install.sh -f ./bigstream-v3.1-0825-0115.tar.gz -p 8030 -d /opt/bigstream_8030

  # 生产部署（多实例：强杀端口 + 强制覆盖 conf/）
  sudo bash bigStream3.0_install.sh -f bigstream-v3.1-0825-0115.tar.gz -p 3000 -d /opt/bigstream2 -k -F

  # nohup 模式（不注册 Supervisor，便于临时验证）
  sudo bash bigStream3.0_install.sh -f ./bigstream-v3.1-0825-0115.tar.gz -m nohup

  # 在线安装最新版
  sudo bash bigStream3.0_install.sh -u https://ai.seek.ninglon.com/release

  # 更新时强制覆盖 conf/（默认不覆盖）
  sudo bash bigStream3.0_install.sh -f ./bigstream-v3.1-XXXX-XXXX.tar.gz -k -F
USAGE
    exit 0
}

# ===== 参数解析 =====
parse_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -f|--file)         LOCAL_FILE="$2"; shift 2 ;;
            -r|--release)      RELEASE_VERSION="$2"; VERSION_SPECIFIED=1; shift 2 ;;
            -u|--url)          RELEASE_URL="$2"; shift 2 ;;
            -p|--port)         PORT="$2"; shift 2 ;;
            -d|--dir)          INSTALL_DIR="$2"; shift 2 ;;
            -k|--kill-port)    KILL_PORT=1; shift ;;
            -F|--force-config) FORCE_CONFIG=1; shift ;;
            -m|--mode)         MODE="$2"; shift 2 ;;
            -n|--dry-run)      DRY_RUN=1; shift ;;
            -V|--version)      echo "$SCRIPT_VERSION"; exit 0 ;;
            -h|--help)         usage ;;
            *) echo "未知参数: $1"; usage ;;
        esac
    done
    if [ -z "$INSTALL_DIR" ]; then INSTALL_DIR="$DEFAULT_INSTALL_DIR"; fi
    if [ "$MODE" != "supervisor" ] && [ "$MODE" != "baota" ] && [ "$MODE" != "nohup" ]; then
        err "无效的部署模式: $MODE（可选: supervisor / baota / nohup）"
        exit 1
    fi
}

# ===== 宝塔环境检测 =====
detect_baota() {
    local BT_PANEL_DIR="/www/server/panel"
    local BT_SUP_PLUGIN="${BT_PANEL_DIR}/plugin/supervisor"

    if [ -d "$BT_PANEL_DIR" ] && [ -d "$BT_SUP_PLUGIN" ]; then
        BT_ENV=1
        BT_PROFILE_DIR="${BT_SUP_PLUGIN}/profile"
        BT_CONFIG_JSON="${BT_SUP_PLUGIN}/config.json"
        BT_STATUS_TXT="${BT_SUP_PLUGIN}/status.txt"
        BT_LOG_DIR="${BT_SUP_PLUGIN}/log"
        BT_SUPCTL="/www/server/panel/pyenv/bin/supervisorctl"
        BT_SUP_CONF="/etc/supervisor/supervisord.conf"
        ok "检测到宝塔面板环境，supervisor 模式将使用宝塔 Supervisor"
    else
        BT_ENV=0
        ok "非宝塔环境，使用标准 Supervisor 模式"
    fi
}

# ===== 前置检查 =====
pre_check() {
    if [ "$(id -u)" -ne 0 ]; then
        err "请使用 root 用户执行"
        echo "  sudo bash $0 $*"
        exit 1
    fi
    local check_dir="$INSTALL_DIR"
    if [ ! -d "$check_dir" ]; then check_dir="$(dirname "$check_dir")"; fi
    local available_mb
    available_mb=$(df -BM "$check_dir" 2>/dev/null | awk 'NR==2{print $4}' | tr -d 'M')
    available_mb="${available_mb:-0}"
    # 实测峰值：tarball 131M + 解压 330M + 运行日志余量；小盘服务器按 600M 卡
    if [ "$available_mb" -lt 600 ]; then
        err "磁盘空间不足 (可用: ${available_mb}MB，需要: 600MB)"
        exit 1
    fi
    # 离线模式跳过 curl 检查（不需要网络下载）
    if [ -z "$LOCAL_FILE" ]; then
        if ! command -v curl >/dev/null 2>&1; then
            err "缺少 curl 命令（离线模式请使用 -f 指定本地安装包）"
            exit 1
        fi
    fi
    ok "前置检查通过"
}

# ===== 从 .env 读取端口 =====
read_port_from_env() {
    local env_file="$INSTALL_DIR/.env"
    if [ -z "$PORT" ] && [ -f "$env_file" ]; then
        local port_val
        port_val=$(grep -oP '^BACKEND_PORT=\K\d+' "$env_file" 2>/dev/null || true)
        if [ -z "$port_val" ]; then
            port_val=$(grep -oP '^PORT=\K\d+' "$env_file" 2>/dev/null || true)
        fi
        if [ -n "$port_val" ]; then
            PORT="$port_val"
            log "从 .env 读取端口: $PORT"
        fi
    fi
    # 尝试从目录名提取端口（如 /opt/bigstream_8030 -> 8030）
    if [ -z "$PORT" ] && [[ "$(basename "$INSTALL_DIR")" =~ _([0-9]+)$ ]]; then
        PORT="${BASH_REMATCH[1]}"
        log "从目录名推断端口: $PORT"
    fi
    if [ -z "$PORT" ]; then PORT="$DEFAULT_PORT"; fi
}

# ===== 获取版本号 =====
fetch_version() {
    # 离线模式：从文件名提取版本号（bigstream-v3.0-0824-0113.tar.gz -> v3.0-0824-0113）
    if [ -n "$LOCAL_FILE" ]; then
        local basename
        basename=$(basename "$LOCAL_FILE")
        RELEASE_VERSION=$(echo "$basename" | sed -E "s/^${PKG_PREFIX}-(.+)\.tar\.gz$/\1/")
        if [ -z "$RELEASE_VERSION" ] || [ "$RELEASE_VERSION" = "$basename" ]; then
            err "无法从文件名提取版本号: $basename"
            echo "  文件名格式应为: ${PKG_PREFIX}-v{版本号}.tar.gz"
            exit 1
        fi
        log "离线模式：从文件名提取版本号: $RELEASE_VERSION"
        return 0
    fi

    if [ -n "$RELEASE_VERSION" ]; then
        log "使用指定版本: $RELEASE_VERSION"
        return 0
    fi

    local latest_md_url="${RELEASE_URL}/bigstream-LATEST.md"
    log "从 $latest_md_url 获取最新版本号..."
    local latest
    latest=$(curl -fsSL --connect-timeout 10 "$latest_md_url" 2>/dev/null | grep -oP '当前最新版本:\s*\K[\w.\-]+' | head -1)

    if [ -z "$latest" ]; then
        err "无法获取最新版本号"
        echo "  请手动指定: sudo bash $0 -r v3.1-XXXX-XXXX"
        echo "  或使用离线模式: sudo bash $0 -f bigstream-v3.1-XXXX-XXXX.tar.gz"
        exit 1
    fi
    RELEASE_VERSION="$latest"
    ok "最新版本: $RELEASE_VERSION (源: $RELEASE_URL)"
}

# ===== 下载部署包 =====
download_package() {
    local tmp_dir
    tmp_dir=$(mktemp -d)

    # 离线模式：从本地 tarball 解压（不删除原始文件）
    if [ -n "$LOCAL_FILE" ]; then
        if [ ! -f "$LOCAL_FILE" ]; then
            err "安装包不存在: $LOCAL_FILE"
            rm -rf "$tmp_dir"
            exit 1
        fi
        log "离线模式：从本地文件解压 $LOCAL_FILE"
        log "安装包大小: $(du -sh "$LOCAL_FILE" | cut -f1)"
        if [ $DRY_RUN -eq 1 ]; then
            log "[DRY-RUN] tar -xzf '$LOCAL_FILE' -C '$tmp_dir'"
            TMP_DIR="$tmp_dir"
            return 0
        fi
        tar -xzf "$LOCAL_FILE" -C "$tmp_dir"
        ok "解压完成（安装包保留在 $LOCAL_FILE）"
        TMP_DIR="$tmp_dir"
        return 0
    fi

    local download_url="${RELEASE_URL}/${PKG_PREFIX}-${RELEASE_VERSION}.tar.gz"
    local archive="$tmp_dir/${PKG_PREFIX}-${RELEASE_VERSION}.tar.gz"
    log "下载部署包到 $archive ..."
    if [ $DRY_RUN -eq 1 ]; then
        log "[DRY-RUN] curl -fsSL '$download_url' -o '$archive'"
        TMP_DIR="$tmp_dir"
        return 0
    fi
    local retry=0
    while [ $retry -lt 3 ]; do
        if curl -fsSL --connect-timeout 30 "$download_url" -o "$archive" 2>/dev/null; then
            ok "下载完成 ($(du -sh "$archive" | cut -f1))"
            break
        fi
        retry=$((retry + 1))
        warn "下载失败，重试 $retry/3 ..."
        sleep 2
    done

    if [ ! -f "$archive" ] || [ ! -s "$archive" ]; then
        err "下载失败: $download_url"
        rm -rf "$tmp_dir"
        exit 1
    fi
    tar -xzf "$archive" -C "$tmp_dir"
    rm -f "$archive"
    TMP_DIR="$tmp_dir"
}

# ===== 端口占用检测 =====
check_port_in_use() {
    local pid
    pid=$(lsof -ti :"$PORT" 2>/dev/null || true)
    if [ -n "$pid" ]; then
        if [ $KILL_PORT -eq 1 ]; then
            warn "端口 $PORT 被占用 (PID: $pid)，强制关闭..."
            if [ $DRY_RUN -eq 1 ]; then
                log "[DRY-RUN] kill -9 $pid"
                return 0
            fi
            kill -9 "$pid" 2>/dev/null || true
            sleep 2
            ok "端口 $PORT 已释放"
        else
            err "端口 $PORT 被占用 (PID: $pid)"
            echo "  请使用 -k 参数强制关闭: sudo bash $0 -k"
            echo "  或更换端口: sudo bash $0 -p 其他端口"
            exit 1
        fi
    fi
}

# ===== 停止旧服务 =====
stop_service() {
    if [ $DRY_RUN -eq 1 ]; then
        log "[DRY-RUN] 停止服务 (端口 $PORT, 模式: $MODE, 宝塔: ${BT_ENV})"
        return 0
    fi
    if [ "$MODE" = "supervisor" ] || [ "$MODE" = "baota" ]; then
        local prog_name="${SUPERVISOR_PROGRAM}_${PORT}"

        if [ "$MODE" = "baota" ] || [ "${BT_ENV:-0}" = "1" ]; then
            log "【宝塔模式】通过 Supervisor 停止 $prog_name..."
            "$BT_SUPCTL" -c "$BT_SUP_CONF" stop "${prog_name}:*" 2>/dev/null || true
        else
            local SUPCTL
            SUPCTL=$(command -v supervisorctl 2>/dev/null || echo "/usr/local/bin/supervisorctl")
            if [ -x "$SUPCTL" ]; then
                log "通过 Supervisor 停止 $prog_name..."
                $SUPCTL stop "$prog_name" 2>/dev/null || true
            else
                warn "未找到 supervisorctl，回退到 kill 方式"
            fi
        fi
    fi
    local pid
    pid=$(lsof -ti :"$PORT" 2>/dev/null || true)
    if [ -n "$pid" ]; then
        log "停止端口 $PORT 上的进程 (PID: $pid)"
        kill "$pid" 2>/dev/null || true
        sleep 2
    fi
}

# ===== 备份当前安装 =====
backup_current() {
    if [ ! -d "$INSTALL_DIR" ]; then
        BACKUP_DIR=""
        return 0
    fi
    BACKUP_DIR="${INSTALL_DIR}.bak.$(date +%Y%m%d%H%M%S)"
    log "备份当前安装到 $BACKUP_DIR ..."
    if [ $DRY_RUN -eq 1 ]; then
        log "[DRY-RUN] cp -r '$INSTALL_DIR' '$BACKUP_DIR'"
        return 0
    fi
    cp -r "$INSTALL_DIR" "$BACKUP_DIR"
    local conf_backed_up="false"
    if [ -d "$BACKUP_DIR/conf" ]; then
        conf_backed_up="true"
        log "conf/ 目录已包含在备份中"
    fi
    echo "version=$(cat "$INSTALL_DIR/VERSION" 2>/dev/null || echo 'unknown')" > "$BACKUP_DIR/.backup_meta"
    echo "backup_time=$(date '+%Y-%m-%d %H:%M:%S')" >> "$BACKUP_DIR/.backup_meta"
    echo "reason=pre-update" >> "$BACKUP_DIR/.backup_meta"
    echo "conf_included=${conf_backed_up}" >> "$BACKUP_DIR/.backup_meta"
    ok "备份完成（conf: ${conf_backed_up}）"
}

# ===== 写入版本历史 =====
write_version_history() {
    local history_file="${INSTALL_DIR}/.version_history"
    local timestamp
    timestamp=$(date +%Y%m%d%H%M%S)
    local action="install"
    if [ -f "$history_file" ]; then
        action="update"
    fi
    echo "${timestamp}|${RELEASE_VERSION}|${action}" >> "$history_file"
    log "版本历史已记录: ${RELEASE_VERSION} (${action})"
}

# ===== 写入部署信息（供 service.sh 读取） =====
write_deploy_info() {
    local info_file="${INSTALL_DIR}/.deploy_info"
    local mode="$MODE"
    if [ "$MODE" = "supervisor" ] && [ "${BT_ENV:-0}" = "1" ]; then mode="baota"; fi
    cat > "$info_file" << DEPLOYEOF
DEPLOY_MODE=${mode}
PORT=${PORT}
INSTALL_DIR=${INSTALL_DIR}
VERSION=${RELEASE_VERSION}
DEPLOY_TIME=$(date '+%Y-%m-%d %H:%M:%S')
SCRIPT_VERSION=${SCRIPT_VERSION}
DEPLOYEOF
    log "部署信息已记录: ${info_file} (模式: ${mode})"
}

# ===== 安装/更新文件 =====
install_or_update() {
    local is_update=0
    if [ -d "$INSTALL_DIR" ] && [ -f "$INSTALL_DIR/bigstream" ]; then is_update=1; fi

    # tarball 根目录为 bigstream-{VERSION}/，自动查找包含 bigstream 可执行文件的目录
    local src_dir="$TMP_DIR/${PKG_PREFIX}-${RELEASE_VERSION}"
    if [ ! -f "$src_dir/bigstream" ]; then
        src_dir=$(find "$TMP_DIR" -maxdepth 2 -name "bigstream" -type f -exec dirname {} \; 2>/dev/null | head -1)
    fi
    if [ -z "$src_dir" ] || [ ! -f "$src_dir/bigstream" ]; then
        src_dir="$TMP_DIR"
    fi
    if [ ! -f "$src_dir/bigstream" ]; then
        err "部署包中未找到 bigstream 可执行文件"
        echo "  TMP_DIR 内容:"
        ls -la "$TMP_DIR/" 2>/dev/null || true
        exit 1
    fi

    if [ $is_update -eq 1 ]; then
        log "===== 更新模式 ====="
        log "旧版本: $(cat "$INSTALL_DIR/VERSION" 2>/dev/null || echo 'unknown')"
        log "新版本: $RELEASE_VERSION"
        log "替换程序文件..."
        if [ $DRY_RUN -eq 1 ]; then
            log "[DRY-RUN] 替换 bigstream, _internal/, ffmpeg/, .env.example, VERSION, service.sh, rollback.sh, start.sh"
            if [ $FORCE_CONFIG -eq 1 ]; then
                log "[DRY-RUN] (-F) 强制覆盖 conf/ 目录"
            else
                log "[DRY-RUN] conf/ 保持不变（未使用 -F）"
            fi
        else
            # 替换可执行文件和依赖
            cp -f "$src_dir/bigstream" "$INSTALL_DIR/bigstream"
            chmod +x "$INSTALL_DIR/bigstream"
            if [ -d "$src_dir/_internal" ]; then rm -rf "${INSTALL_DIR}/_internal"; cp -r "$src_dir/_internal" "$INSTALL_DIR/_internal"; fi

            # conf 目录处理（摄像头配对运行数据，默认不覆盖）
            if [ $FORCE_CONFIG -eq 1 ]; then
                if [ -d "$src_dir/conf" ]; then
                    warn "强制覆盖 conf/ 目录（-F 参数）"
                    rm -rf "${INSTALL_DIR}/conf"
                    cp -r "$src_dir/conf" "$INSTALL_DIR/conf"
                    ok "conf/ 已强制覆盖（旧配置已备份，可通过 rollback.sh 回滚）"
                fi
            else
                log "conf/ 保持不变（如需同步新版配对数据，使用 -F 强制覆盖）"
            fi

            # .env.example 始终更新为最新模板
            if [ -f "$src_dir/.env.example" ]; then cp -f "$src_dir/.env.example" "$INSTALL_DIR/.env.example"; fi

            # VERSION / service.sh / rollback.sh / start.sh
            if [ -f "$src_dir/VERSION" ]; then cp "$src_dir/VERSION" "$INSTALL_DIR/VERSION"; fi
            if [ -f "$src_dir/rollback.sh" ]; then cp "$src_dir/rollback.sh" "$INSTALL_DIR/rollback.sh"; chmod +x "$INSTALL_DIR/rollback.sh"; fi
            if [ -f "$src_dir/service.sh" ]; then cp "$src_dir/service.sh" "$INSTALL_DIR/service.sh"; chmod +x "$INSTALL_DIR/service.sh"; fi
            if [ -f "$src_dir/start.sh" ]; then cp "$src_dir/start.sh" "$INSTALL_DIR/start.sh"; chmod +x "$INSTALL_DIR/start.sh"; fi

            # 静态 ffmpeg（RTSP→FLV 转码，v3.1-0825-0116 起内置；老包没有则保持现状）
            if [ -f "$src_dir/ffmpeg/ffmpeg" ]; then
                rm -rf "${INSTALL_DIR}/ffmpeg"
                mkdir -p "$INSTALL_DIR/ffmpeg"
                cp "$src_dir/ffmpeg/ffmpeg" "$INSTALL_DIR/ffmpeg/ffmpeg"
                chmod +x "$INSTALL_DIR/ffmpeg/ffmpeg"
                log "已更新内置静态 ffmpeg: $($INSTALL_DIR/ffmpeg/ffmpeg -version 2>&1 | head -1)"
            else
                log "包内无静态 ffmpeg（保持现有 ffmpeg/ 或回退系统 PATH）"
            fi

            # .env 不覆盖（保留各环境独立配置）
            log ".env 保持不变（如需更新配置，请手动编辑 .env）"
        fi
    else
        log "===== 全新安装 ====="
        log "版本: $RELEASE_VERSION | 目录: $INSTALL_DIR"
        if [ $DRY_RUN -eq 1 ]; then
            log "[DRY-RUN] mkdir -p '$INSTALL_DIR' 并复制所有文件"
        else
            mkdir -p "$INSTALL_DIR"
            cp -r "$src_dir/bigstream" "$INSTALL_DIR/"
            chmod +x "$INSTALL_DIR/bigstream"
            if [ -d "$src_dir/_internal" ]; then cp -r "$src_dir/_internal" "$INSTALL_DIR/"; fi
            if [ -d "$src_dir/ffmpeg" ]; then cp -r "$src_dir/ffmpeg" "$INSTALL_DIR/"; chmod +x "$INSTALL_DIR/ffmpeg/ffmpeg" 2>/dev/null || true; fi
            if [ -d "$src_dir/conf" ]; then cp -r "$src_dir/conf" "$INSTALL_DIR/"; fi
            if [ -f "$src_dir/.env.example" ]; then cp "$src_dir/.env.example" "$INSTALL_DIR/"; fi
            if [ -f "$src_dir/VERSION" ]; then cp "$src_dir/VERSION" "$INSTALL_DIR/"; fi
            if [ -f "$src_dir/rollback.sh" ]; then cp "$src_dir/rollback.sh" "$INSTALL_DIR/rollback.sh"; chmod +x "$INSTALL_DIR/rollback.sh"; fi
            if [ -f "$src_dir/service.sh" ]; then cp "$src_dir/service.sh" "$INSTALL_DIR/service.sh"; chmod +x "$INSTALL_DIR/service.sh"; fi
            if [ -f "$src_dir/start.sh" ]; then cp "$src_dir/start.sh" "$INSTALL_DIR/start.sh"; chmod +x "$INSTALL_DIR/start.sh"; fi

            # 首次安装：从 .env.example 复制为 .env
            if [ -f "$INSTALL_DIR/.env.example" ] && [ ! -f "$INSTALL_DIR/.env" ]; then
                cp "$INSTALL_DIR/.env.example" "$INSTALL_DIR/.env"
                warn "已从 .env.example 创建 .env，请编辑填入实际配置："
                warn "  vi $INSTALL_DIR/.env"
            fi
        fi
    fi
    ok "文件部署完成"
}

# ===== 创建 systemd 服务文件（宝塔环境用） =====
create_systemd_service() {
    cat > /etc/systemd/system/supervisord.service <<'EOF'
[Unit]
Description=Process Monitoring and Control Daemon
After=network.target

[Service]
Type=forking
ExecStart=/www/server/panel/pyenv/bin/supervisord -c /etc/supervisor/supervisord.conf
ExecStop=/www/server/panel/pyenv/bin/supervisorctl -c /etc/supervisor/supervisord.conf shutdown
ExecReload=/www/server/panel/pyenv/bin/supervisorctl -c /etc/supervisor/supervisord.conf reload
KillMode=process
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
EOF
    systemctl daemon-reload
    systemctl enable supervisord
    ok "systemd 服务文件已创建"
}

# ===== 更新宝塔 config.json =====
update_baota_config_json() {
    local prog_name="$1"
    local cmd="${INSTALL_DIR}/bigstream --host 0.0.0.0 --port ${PORT}"

    /www/server/panel/pyenv/bin/python3 -c "
import json

CONFIG_PATH = '${BT_CONFIG_JSON}'
prog_name = '${prog_name}'
command = '${cmd}'
directory = '${INSTALL_DIR}'

new_process = {
    'program': prog_name,
    'command': command,
    'directory': directory,
    'user': 'root',
    'priority': '1',
    'numprocs': '1',
    'runStatus': 'RUNNING',
    'ps': prog_name
}

try:
    config = json.load(open(CONFIG_PATH))
except:
    config = []

config = [c for c in config if c.get('program') != prog_name]
config.append(new_process)

json.dump(config, open(CONFIG_PATH, 'w'), ensure_ascii=False, indent=2)
print(f'config.json updated: {prog_name}')
" 2>/dev/null || warn "config.json 更新失败（不影响进程启动，面板可能需手动刷新）"
}

# ===== 宝塔模式启动 =====
start_service_baota() {
    local prog_name="${SUPERVISOR_PROGRAM}_${PORT}"
    local ini_file="${BT_PROFILE_DIR}/${prog_name}.ini"

    log "【宝塔模式】通过 Supervisor 启动 $prog_name..."

    if [ ! -f "/etc/systemd/system/supervisord.service" ]; then
        warn "未找到 supervisord systemd 服务文件，正在创建..."
        create_systemd_service
    fi

    if ! systemctl is-active --quiet supervisord 2>/dev/null; then
        warn "supervisord 未通过 systemd 运行，正在修复..."
        pkill -9 supervisord 2>/dev/null || true
        rm -f /var/run/supervisor.sock /var/run/supervisord.pid
        systemctl daemon-reload
        systemctl enable supervisord
        systemctl start supervisord
        sleep 3
    fi

    local old_conf="/etc/supervisor/conf.d/${prog_name}.conf"
    if [ -f "$old_conf" ]; then
        warn "发现旧配置 $old_conf，正在清理..."
        rm -f "$old_conf"
    fi

    if [ -f "$ini_file" ]; then
        log "检测到已有配置 $ini_file，执行更新..."
        cp "$ini_file" "${ini_file}.bak.$(date +%s)" 2>/dev/null || true
    fi

    mkdir -p "$BT_PROFILE_DIR" "$BT_LOG_DIR"
    cat > "$ini_file" << SUPCONF
[program:${prog_name}]
command=${INSTALL_DIR}/bigstream --host 0.0.0.0 --port ${PORT}
process_name=%(program_name)s_%(process_num)02d
numprocs=1
directory=${INSTALL_DIR}
user=root
autostart=true
autorestart=true
startsecs=5
startretries=3
stopwaitsecs=10
killasgroup=true
stopasgroup=true
stdout_logfile=${BT_LOG_DIR}/${prog_name}.out.log
stderr_logfile=${BT_LOG_DIR}/${prog_name}.err.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=3
stderr_logfile_maxbytes=10MB
stderr_logfile_backups=3
SUPCONF

    log "宝塔 Supervisor 配置已写入: $ini_file"

    "$BT_SUPCTL" -c "$BT_SUP_CONF" reread
    "$BT_SUPCTL" -c "$BT_SUP_CONF" update
    sleep 3
    "$BT_SUPCTL" -c "$BT_SUP_CONF" start "${prog_name}:*" 2>/dev/null || true

    update_baota_config_json "$prog_name"

    echo -n '' > "$BT_STATUS_TXT" 2>/dev/null || true

    ok "宝塔 Supervisor 进程已启动，面板可见"
}

# ===== 标准模式启动 =====
start_service_standard() {
    local prog_name="${SUPERVISOR_PROGRAM}_${PORT}"
    local SUPCTL
    SUPCTL=$(command -v supervisorctl 2>/dev/null || echo "/usr/local/bin/supervisorctl")

    if [ ! -x "$SUPCTL" ]; then
        warn "未找到 supervisorctl，回退到 nohup 方式"
        cd "$INSTALL_DIR"
        nohup ./bigstream --host 0.0.0.0 --port "$PORT" > "${INSTALL_DIR}/bigstream.log" 2>&1 &
        echo "$!" > "${INSTALL_DIR}/bigstream.pid"
        return 0
    fi

    log "通过 Supervisor 启动 $prog_name..."
    local SUP_CONF_DIR="/etc/supervisor/conf.d"
    mkdir -p "$SUP_CONF_DIR"
    local SUP_CONF="$SUP_CONF_DIR/${prog_name}.conf"

    cat > "$SUP_CONF" << SUPCONF
[program:${prog_name}]
command=${INSTALL_DIR}/bigstream --host 0.0.0.0 --port ${PORT}
process_name=%(program_name)s_%(process_num)02d
numprocs=1
directory=${INSTALL_DIR}
autostart=true
autorestart=true
startsecs=5
startretries=3
stopwaitsecs=10
killasgroup=true
stopasgroup=true
stdout_logfile=${INSTALL_DIR}/bigstream.out.log
stderr_logfile=${INSTALL_DIR}/bigstream.err.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=3
SUPCONF

    log "Supervisor 配置已写入: $SUP_CONF"
    $SUPCTL reread
    $SUPCTL update
    sleep 3
    $SUPCTL start "$prog_name" 2>/dev/null || true
}

# ===== 启动服务 =====
start_service() {
    if [ $DRY_RUN -eq 1 ]; then
        log "[DRY-RUN] 启动服务 (端口 $PORT, 模式: $MODE, 宝塔: ${BT_ENV})"
        return 0
    fi

    if [ "$MODE" = "baota" ]; then
        start_service_baota
        return 0
    fi
    if [ "$MODE" = "supervisor" ]; then
        if [ "${BT_ENV:-0}" = "1" ]; then
            start_service_baota
        else
            start_service_standard
        fi
        return 0
    fi

    # nohup 模式
    cd "$INSTALL_DIR"
    log "通过 nohup 启动..."
    nohup ./bigstream --host 0.0.0.0 --port "$PORT" > "${INSTALL_DIR}/bigstream.log" 2>&1 &
    echo "$!" > "${INSTALL_DIR}/bigstream.pid"
}

# ===== 健康检查 =====
health_check() {
    # v3.1-0827-0126 起整站挂 /bigscreen/ 子路径，旧 /api/health 返回 301；-L 跟随后新旧版本均得 200
    local health_url="http://localhost:${PORT}/api/health"
    if [ $DRY_RUN -eq 1 ]; then
        log "[DRY-RUN] 健康检查: curl $health_url"
        return 0
    fi
    local retry=0
    local resp
    while [ $retry -lt 10 ]; do
        retry=$((retry + 1))
        resp=$(curl -sL -o /dev/null -w "%{http_code}" "$health_url" 2>/dev/null || echo "000")
        # 大屏免认证，健康检查只认 200
        if [ "$resp" = "200" ]; then
            ok "健康检查通过 (HTTP $resp)"
            return 0
        fi
        log "等待启动... ($retry/10, HTTP $resp)"
        sleep 3
    done
    return 1
}

# ===== 回滚 =====
rollback() {
    if [ -z "${BACKUP_DIR:-}" ] || [ ! -d "$BACKUP_DIR" ]; then
        err "无备份可回滚"
        return 1
    fi
    warn "更新失败，执行回滚（含 conf/ 配置）..."
    rm -rf "$INSTALL_DIR"
    mv "$BACKUP_DIR" "$INSTALL_DIR"
    if [ -d "$INSTALL_DIR/conf" ]; then
        log "conf/ 目录已随回滚恢复"
    fi
    start_service
    if health_check; then
        ok "回滚成功（含 conf/ 配置）"
    else
        err "回滚后服务仍未正常启动，请手动排查"
    fi
}

# ===== 清理 =====
cleanup() {
    if [ -n "${TMP_DIR:-}" ] && [ -d "${TMP_DIR}" ]; then
        rm -rf "$TMP_DIR"
    fi
}

# ===== 主函数 =====
main() {
    parse_args "$@"
    echo ""
    echo "============================================"
    echo "  BigStream3.0 大屏系统 通用安装"
    echo "  版本: ${RELEASE_VERSION:-自动获取}"
    echo "  目录: $INSTALL_DIR"
    echo "  端口: ${PORT:-从.env读取}"
    echo "  守护: $MODE"
    echo "  强制覆盖conf: $([ $FORCE_CONFIG -eq 1 ] && echo '是' || echo '否')"
    if [ -n "$LOCAL_FILE" ]; then
        echo "  模式: 离线安装（本地文件: $LOCAL_FILE）"
    else
        echo "  模式: 在线安装（源: $RELEASE_URL）"
    fi
    echo "============================================"
    echo ""
    pre_check
    detect_baota
    read_port_from_env
    fetch_version
    download_package
    check_port_in_use
    if [ -d "$INSTALL_DIR" ] && [ -f "$INSTALL_DIR/bigstream" ]; then
        stop_service
        backup_current
        install_or_update
        write_version_history
        write_deploy_info
        start_service
        if health_check; then
            ok "更新成功！版本: $RELEASE_VERSION"
        else
            rollback
            exit 1
        fi
    else
        install_or_update
        # 首次安装需要用户先配置 .env（模板带 your-/YOUR_ 占位符）
        if [ ! -f "$INSTALL_DIR/.env" ] || [ "$(grep -cE 'your-|YOUR_' "$INSTALL_DIR/.env" 2>/dev/null || echo 0)" -gt 0 ]; then
            warn "首次安装，请先编辑配置文件："
            warn "  vi $INSTALL_DIR/.env"
            warn "  填入实际的数据库、视频总线等配置后，重新运行安装脚本启动服务"
            write_version_history
            write_deploy_info
            echo ""
            echo "============================================"
            echo "  文件已部署，等待配置 .env"
            echo "  目录: $INSTALL_DIR"
            echo "  配置: $INSTALL_DIR/.env"
            echo "============================================"
            echo ""
            cleanup
            exit 0
        fi
        write_version_history
        write_deploy_info
        start_service
        if health_check; then
            ok "安装成功！版本: $RELEASE_VERSION"
        else
            err "安装后健康检查失败"
            exit 1
        fi
    fi
    echo ""
    echo "============================================"
    echo "  部署完成"
    echo "  版本: $(cat "$INSTALL_DIR/VERSION" 2>/dev/null || echo $RELEASE_VERSION)"
    echo "  目录: $INSTALL_DIR"
    echo "  端口: $PORT"
    echo "  日志: ${INSTALL_DIR}/logs/bigstream.log"
    echo ""
    echo "  服务管理: sudo bash ${INSTALL_DIR}/service.sh {start|stop|restart|status|log|health}"
    echo "  版本回滚: sudo bash ${INSTALL_DIR}/rollback.sh"
    echo "============================================"
    echo ""
    cleanup
}

trap cleanup EXIT
main "$@"
