#!/bin/bash
# Passport Web + Certificate Pipeline 安装脚本
# 用于全新机器部署 /data/onlyface 环境
# 需要 root 权限运行: bash install_all.sh

set -euo pipefail

# ========== 配置区域 ==========
BASE_DIR="/data/onlyface"
MODEL_DIR="$BASE_DIR/models"
TEMPLATE_DIR="$BASE_DIR/templates"
WEB_DIR="$BASE_DIR/web"
VENV_DIR="$BASE_DIR/venv"
PYTHON_VERSION="3.11"
MIN_RAM_GB=16  # 推荐内存

# 镜像源配置（国内机器建议开启）
USE_CN_MIRROR=true
PIP_INDEX="https://pypi.org/simple"
APT_MIRROR="http://mirrors.aliyun.com/ubuntu"

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

log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
log_ok() { echo -e "${GREEN}[OK]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_err() { echo -e "${RED}[ERR]${NC} $*"; }

# ========== 检查 root ==========
check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_err "请使用 root 权限运行: sudo bash $0"
        exit 1
    fi
}

# ========== 检查系统 ==========
check_system() {
    log_info "检查系统环境..."
    
    # Ubuntu/Debian 检查
    if [[ ! -f /etc/os-release ]]; then
        log_err "无法识别操作系统"
        exit 1
    fi
    source /etc/os-release
    log_info "OS: $PRETTY_NAME"
    
    # 内存检查
    TOTAL_MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    TOTAL_MEM_MB=$((TOTAL_MEM_KB / 1024))
    TOTAL_MEM_GB=$((TOTAL_MEM_MB / 1024))
    if [[ $TOTAL_MEM_GB -lt $MIN_RAM_GB ]]; then
        log_warn "内存 ${TOTAL_MEM_MB}MB (${TOTAL_MEM_GB}GB) < 推荐 ${MIN_RAM_GB}GB，可能触发 OOM"
        log_warn "建议至少 16GB 内存 + 8GB Swap"
    else
        log_ok "内存 ${TOTAL_MEM_GB}GB 满足要求"
    fi
    
    # 磁盘检查
    DISK_AVAIL_GB=$(df -BG "$BASE_DIR" 2>/dev/null | tail -1 | awk '{print $4}' | sed 's/G//' || echo "50")
    if [[ $DISK_AVAIL_GB -lt 20 ]]; then
        log_warn "磁盘可用空间 ${DISK_AVAIL_GB}GB < 20GB，可能不足"
    fi
    
    # 架构检查
    ARCH=$(uname -m)
    if [[ "$ARCH" != "x86_64" ]]; then
        log_warn "架构 $ARCH 非 x86_64，部分模型可能不兼容"
    fi
}

# ========== 配置镜像源 ==========
setup_mirrors() {
    if [[ "$USE_CN_MIRROR" == "true" ]]; then
        log_info "配置国内镜像源..."
        
        # apt 镜像
        if [[ -f /etc/apt/sources.list ]]; then
            cp /etc/apt/sources.list /etc/apt/sources.list.bak
            sed -i "s|http://archive.ubuntu.com/ubuntu|$APT_MIRROR|g" /etc/apt/sources.list
            sed -i "s|http://security.ubuntu.com/ubuntu|$APT_MIRROR|g" /etc/apt/sources.list
        fi
        
        # pip 镜像
        PIP_INDEX="https://mirrors.aliyun.com/pypi/simple"
        export PIP_INDEX_URL="$PIP_INDEX"
    fi
}

# ========== 安装系统依赖 ==========
install_system_deps() {
    log_info "安装系统依赖包..."
    
    # Detect package manager
    if command -v apt-get &>/dev/null; then
        PKG_MGR="apt-get"
        PKG_UPDATE="apt-get update -y"
        PKG_INSTALL="apt-get install -y"
        export DEBIAN_FRONTEND=noninteractive
    elif command -v dnf &>/dev/null; then
        PKG_MGR="dnf"
        PKG_UPDATE="dnf makecache -y"
        PKG_INSTALL="dnf install -y"
    elif command -v yum &>/dev/null; then
        PKG_MGR="yum"
        PKG_UPDATE="yum makecache -y"
        PKG_INSTALL="yum install -y"
    else
        log_err "未找到支持的包管理器 (apt/dnf/yum)"
        exit 1
    fi
    
    log_info "使用包管理器: $PKG_MGR"
    
    $PKG_UPDATE
    
    # Common packages (names may differ slightly between distros)
    if [[ "$PKG_MGR" == "apt-get" ]]; then
        $PKG_INSTALL \
            python3 python3-venv python3-dev python3-pip \
            build-essential cmake pkg-config \
            libglib2.0-0 libsm6 libxext6 libxrender-dev libgl1-mesa-glx \
            libopencv-dev ffmpeg \
            wget curl git unzip \
            fonts-dejavu-core \
            libgomp1 \
            2>&1 | tail -20
    else
        # dnf/yum (RHEL/CentOS/Anolis/Alinux)
        # Note: package names differ from Ubuntu
        $PKG_INSTALL \
            python3 python3-devel python3-pip \
            gcc gcc-c++ make cmake pkgconfig \
            glib2 libSM libXext libXrender mesa-libGL \
            opencv-devel \
            wget curl git unzip \
            dejavu-sans-fonts \
            libgomp \
            2>&1 | tail -20
        
        # Install ffmpeg from rpmfusion or epel
        if ! command -v ffmpeg &>/dev/null; then
            log_info "安装 ffmpeg (从 epel)..."
            $PKG_INSTALL -y epel-release 2>/dev/null || true
            $PKG_INSTALL -y ffmpeg 2>/dev/null || \
                log_warn "ffmpeg 安装失败，请手动安装"
        fi
        
        # python3-venv is usually included in python3 package on RHEL/CentOS
    fi
    
    log_ok "系统依赖安装完成"
}

# ========== 创建目录结构 ==========
create_dirs() {
    log_info "创建目录结构..."
    mkdir -p "$MODEL_DIR" "$TEMPLATE_DIR" "$WEB_DIR/uploads" "$WEB_DIR/results" \
             "$BASE_DIR/pin" "$BASE_DIR/pout" "$BASE_DIR/checkpoints"
    chmod 755 "$BASE_DIR"
    log_ok "目录创建完成"
}

# ========== 创建虚拟环境 ==========
create_venv() {
    log_info "创建 Python 虚拟环境..."
    python3 -m venv "$VENV_DIR" --symlinks
    "$VENV_DIR/bin/pip" install --upgrade pip -i "$PIP_INDEX"
    log_ok "虚拟环境就绪: $VENV_DIR"
}

# ========== 安装 Python 依赖 ==========
install_python_deps() {
    log_info "安装 Python 依赖（可能需要几分钟）..."
    
    # Detect Python version for compatibility
    PY_VER=$("$VENV_DIR/bin/python" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
    log_info "Python 版本: $PY_VER"
    
    # Choose compatible versions based on Python version
    if [[ "$PY_VER" == "3.6" ]]; then
        # Python 3.6 compatible versions
        NUMPY_VER="numpy==1.19.5"
        OPENCV_VER="opencv-python==4.5.5.64"
        PILLOW_VER="Pillow==9.5.0"
        TORCH_VER="torch==1.13.1+cpu"
        TORCHVISION_VER="torchvision==0.14.1+cpu"
        TORCHAUDIO_VER="torchaudio==0.13.1+cpu"
        INSIGHTFACE_VER="insightface==0.7.3"
        REMBG_VER="rembg==1.0.19"
        EASYOCR_VER="easyocr==1.6.2"
        PADDLE_VER="paddlepaddle==2.4.2"
        PADDLEOCR_VER="paddleocr==2.7.0.3"
    elif [[ "$PY_VER" == "3.7" ]]; then
        NUMPY_VER="numpy==1.24.3"
        OPENCV_VER="opencv-python==4.8.1.78"
        PILLOW_VER="Pillow==10.0.0"
        TORCH_VER="torch==2.0.1+cpu"
        TORCHVISION_VER="torchvision==0.15.2+cpu"
        TORCHAUDIO_VER="torchaudio==2.0.2+cpu"
        INSIGHTFACE_VER="insightface==0.7.3"
        REMBG_VER="rembg==2.0.0"
        EASYOCR_VER="easyocr==1.7.0"
        PADDLE_VER="paddlepaddle==2.5.1"
        PADDLEOCR_VER="paddleocr==2.7.0.3"
    else
        # Python 3.8+ (default)
        NUMPY_VER="numpy==2.3.5"
        OPENCV_VER="opencv-python==4.10.0.84"
        PILLOW_VER="Pillow==10.0.0"
        TORCH_VER="torch==2.14.0+cpu"
        TORCHVISION_VER="torchvision==0.29.0+cpu"
        TORCHAUDIO_VER="torchaudio==2.11.0+cpu"
        INSIGHTFACE_VER="insightface==1.0.1"
        REMBG_VER="rembg==2.0.83"
        EASYOCR_VER="easyocr==1.7.2"
        PADDLE_VER="paddlepaddle==3.3.1"
        PADDLEOCR_VER="paddleocr==3.7.0"
    fi
    
    # 先装核心依赖，避免冲突
    "$VENV_DIR/bin/pip" install -i "$PIP_INDEX" \
        $NUMPY_VER \
        $OPENCV_VER \
        $PILLOW_VER \
        tqdm==4.65.0 \
        scikit-image==0.21.0 \
        onnxruntime==1.16.0 \
        2>&1 | tail -10
    
    # PyTorch CPU
    "$VENV_DIR/bin/pip" install -i "$PIP_INDEX" \
        $TORCH_VER $TORCHVISION_VER $TORCHAUDIO_VER \
        --index-url https://download.pytorch.org/whl/cpu \
        2>&1 | tail -10
    
    # InsightFace + 相关
    "$VENV_DIR/bin/pip" install -i "$PIP_INDEX" \
        $INSIGHTFACE_VER \
        $REMBG_VER \
        $EASYOCR_VER \
        2>&1 | tail -10
    
    # PaddlePaddle + PaddleOCR (CPU)
    "$VENV_DIR/bin/pip" install -i https://www.paddlepaddle.org.cn/packages/stable/cpu/ \
        $PADDLE_VER \
        2>&1 | tail -10
    
    "$VENV_DIR/bin/pip" install -i "$PIP_INDEX" \
        $PADDLEOCR_VER \
        2>&1 | tail -10
    
    # Flask + Gunicorn
    "$VENV_DIR/bin/pip" install -i "$PIP_INDEX" \
        flask==3.1.3 \
        gunicorn==26.2.0 \
        2>&1 | tail -10
    
    log_ok "Python 依赖安装完成"
}

# ========== 下载模型 ==========
download_models() {
    log_info "下载模型文件（大文件，请耐心等待）..."
    
    cd "$MODEL_DIR"
    
    # inswapper_128.onnx (554MB)
    if [[ ! -f "inswapper_128.onnx" || ! -s "inswapper_128.onnx" ]]; then
        log_info "下载 inswapper_128.onnx..."
        wget -q --show-progress -O inswapper_128.onnx \
            "https://github.com/deepinsight/insightface/releases/download/v0.7/inswapper_128.onnx" \
            || { log_err "inswapper 下载失败"; exit 1; }
    else
        log_ok "inswapper_128.onnx 已存在"
    fi
    
    # silueta.onnx (44MB)
    if [[ ! -f "silueta.onnx" || ! -s "silueta.onnx" ]]; then
        log_info "下载 silueta.onnx..."
        wget -q --show-progress -O silueta.onnx \
            "https://github.com/danielgatis/rembg/releases/download/v0.0.0/silueta.onnx" \
            || { log_err "silueta 下载失败"; exit 1; }
    else
        log_ok "silueta.onnx 已存在"
    fi
    
    # big-lama.pt (206MB)
    if [[ ! -f "big-lama.pt" || ! -s "big-lama.pt" ]]; then
        log_info "下载 big-lama.pt..."
        wget -q --show-progress -O big-lama.pt \
            "https://github.com/Sanster/models/releases/download/add_big_lama/big-lama.pt" \
            || { log_err "LaMa 下载失败"; exit 1; }
    else
        log_ok "big-lama.pt 已存在"
    fi
    
    # birefnet-general.onnx (973MB) - 可选，GPU用
    if [[ ! -f "birefnet-general.onnx" || ! -s "birefnet-general.onnx" ]]; then
        log_info "下载 birefnet-general.onnx..."
        wget -q --show-progress -O birefnet-general.onnx \
            "https://github.com/danielgatis/rembg/releases/download/v0.0.0/BiRefNet-general-epoch_244.onnx" \
            || log_warn "BiRefNet 下载失败（可选，CPU模式不需要）"
    else
        log_ok "birefnet-general.onnx 已存在"
    fi
    
    # 创建软链接兼容旧代码
    ln -sf birefnet-general.onnx BiRefNet-general-epoch_244.onnx 2>/dev/null || true
    
    # 创建空的 rmbg-2.0.onnx 占位
    touch rmbg-2.0.onnx 2>/dev/null || true
    
    log_ok "模型下载完成"
    ls -lh "$MODEL_DIR"/*.onnx "$MODEL_DIR"/*.pt 2>/dev/null
}

# ========== 部署 Web 应用 ==========
deploy_webapp() {
    log_info "部署 Web 应用..."
    
    # 复制代码
    if [[ -f "/root/passport_web.tar.gz" ]]; then
        tar -xzf /root/passport_web.tar.gz -C /tmp/
        cp -r /tmp/web/* "$WEB_DIR/"
    elif [[ -d "$WEB_DIR" && -f "$WEB_DIR/app.py" ]]; then
        log_info "Web 代码已存在，跳过复制"
    else
        log_err "未找到 Web 源码，请先放置 passport_web.tar.gz 到 /root/"
        exit 1
    fi
    
    # 复制本地模块
    cp /data/onlyface/mrz.py "$WEB_DIR/" 2>/dev/null || true
    cp /data/onlyface/bisnet.py "$WEB_DIR/" 2>/dev/null || true
    cp /data/onlyface/certificate_pipeline.py "$BASE_DIR/" 2>/dev/null || true
    
    # 创建模板图
    if [[ ! -f "$TEMPLATE_DIR/mm_girl.jpg" ]]; then
        log_info "生成护照模板图..."
        "$VENV_DIR/bin/python" -c "
import cv2, numpy as np
w, h = 905, 1280
img = np.full((h, w, 3), (235, 229, 217), dtype=np.uint8)
cv2.rectangle(img, (4, 4), (w-5, h-5), (107, 91, 61), 4)
cv2.rectangle(img, (10, 10), (w-11, h-11), (139, 123, 93), 1)
cv2.ellipse(img, (w//2, 60), (35, 45), 0, 0, 360, (107, 91, 61), 2)
cv2.putText(img, 'MYANMAR', (w//2 - 40, 115), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (107, 91, 61), 2)
cv2.putText(img, 'REPUBLIC OF THE UNION OF MYANMAR', (w//2 - 300, 140), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (26, 26, 26), 2)
cv2.putText(img, 'P A S S P O R T', (w//2 - 150, 180), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (107, 0, 0), 2)
# Left photo
cv2.rectangle(img, (47, 217), (347, 617), (139, 123, 93), 2)
cv2.putText(img, 'PHOTO', (160, 420), cv2.FONT_HERSHEY_SIMPLEX, 1, (170, 170, 170), 2)
# Right photo
cv2.rectangle(img, (448, 248), (568, 408), (139, 123, 93), 1)
cv2.imwrite('$TEMPLATE_DIR/mm_girl.jpg', img)
print('Template created')
"
    fi
    
    log_ok "Web 应用部署完成"
}

# ========== 验证安装 ==========
verify_install() {
    log_info "验证安装..."
    
    # 测试导入
    "$VENV_DIR/bin/python" -c "
import sys
sys.path.insert(0, '/data/onlyface')
import cv2, numpy, torch, onnxruntime, insightface, rembg, easyocr, flask, gunicorn
print('All imports OK')
print(f'OpenCV: {cv2.__version__}')
print(f'Torch: {torch.__version__}')
print(f'ORT: {onnxruntime.__version__}')
print(f'InsightFace: {insightface.__version__}')
print(f'EasyOCR: {easyocr.__version__}')
"
    
    # 测试模型加载
    "$VENV_DIR/bin/python" -c "
import sys
sys.path.insert(0, '/data/onlyface')
from insightface.app import FaceAnalysis
app = FaceAnalysis(name='buffalo_l', root='/data/onlyface/models', providers=['CPUExecutionProvider'])
app.prepare(ctx_id=-1, det_size=(640, 640))
print('FaceAnalysis OK')

import insightface
swapper = insightface.model_zoo.get_model('/data/onlyface/models/inswapper_128.onnx', providers=['CPUExecutionProvider'])
print('InSwapper OK')

import onnxruntime as ort
sess = ort.InferenceSession('/data/onlyface/models/silueta.onnx', providers=['CPUExecutionProvider'])
print('Silueta OK')

import torch
lama = torch.jit.load('/data/onlyface/models/big-lama.pt', map_location='cpu')
print('LaMa OK')
"
    
    log_ok "验证通过"
}

# ========== 创建 systemd 服务 ==========
create_service() {
    log_info "创建 systemd 服务..."
    
    cat > /etc/systemd/system/passport-web.service <<EOF
[Unit]
Description=Passport Web Service
After=network.target
Wants=network.target

[Service]
Type=notify
User=root
WorkingDirectory=$WEB_DIR
Environment=PATH=$VENV_DIR/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=PYTHONPATH=/data/onlyface
ExecStart=$VENV_DIR/bin/gunicorn -w 1 -b 0.0.0.0:7862 app:app \\
    --timeout 600 --graceful-timeout 600 \\
    --worker-class sync --max-requests 1000 --max-requests-jitter 100 \\
    --access-logfile /var/log/passport-web-access.log \\
    --error-logfile /var/log/passport-web-error.log
ExecReload=/bin/kill -s HUP \$MAINPID
KillMode=mixed
Restart=on-failure
RestartSec=10
LimitNOFILE=65536
MemoryLimit=12G

[Install]
WantedBy=multi-user.target
EOF
    
    # 日志目录
    mkdir -p /var/log
    touch /var/log/passport-web-access.log /var/log/passport-web-error.log
    chmod 644 /var/log/passport-web-*.log
    
    systemctl daemon-reload
    systemctl enable passport-web.service
    
    log_ok "systemd 服务创建完成: passport-web.service"
}

# ========== 启动服务 ==========
start_service() {
    log_info "启动服务..."
    systemctl start passport-web.service
    sleep 5
    
    if systemctl is-active --quiet passport-web.service; then
        log_ok "服务启动成功"
        systemctl status passport-web.service --no-pager
    else
        log_err "服务启动失败"
        journalctl -u passport-web.service --no-pager -n 30
        exit 1
    fi
}

# ========== 健康检查 ==========
health_check() {
    log_info "健康检查..."
    sleep 3
    
    for i in {1..10}; do
        if curl -sf http://127.0.0.1:7862/random_passport_data >/dev/null; then
            log_ok "API 响应正常"
            curl -s http://127.0.0.1:7862/random_passport_data | head -c 200
            echo
            return 0
        fi
        sleep 2
    done
    
    log_err "健康检查失败"
    return 1
}

# ========== 显示部署信息 ==========
show_summary() {
    echo
    echo "=========================================="
    echo -e "${GREEN}部署完成！${NC}"
    echo "=========================================="
    echo
    echo "安装路径: $BASE_DIR"
    echo "模型目录: $MODEL_DIR"
    echo "Web目录:  $WEB_DIR"
    echo "虚拟环境: $VENV_DIR"
    echo
    echo "服务管理:"
    echo "  systemctl start passport-web    # 启动"
    echo "  systemctl stop passport-web     # 停止"
    echo "  systemctl restart passport-web  # 重启"
    echo "  systemctl status passport-web   # 状态"
    echo "  journalctl -u passport-web -f   # 查看日志"
    echo
    echo "访问地址:"
    echo "  http://\$(hostname -I | awk '{print \$1}'):7862"
    echo "  http://localhost:7862"
    echo
    echo "API 端点:"
    echo "  GET  /random_passport_data   - 随机护照数据"
    echo "  POST /compute_mrz            - 计算 MRZ"
    echo "  POST /generate_passport      - 生成护照（含换脸）"
    echo "  POST /make_id_photo          - 制作证件照"
    echo "  POST /smart_crop_source      - 智能裁切"
    echo
    echo "证件管道 CLI:"
    echo "  cd $BASE_DIR && ./venv/bin/python certificate_pipeline.py --help"
    echo
    echo -e "${YELLOW}注意: 建议服务器内存 ≥16GB + 8GB Swap，否则可能触发 OOM${NC}"
}

# ========== 主流程 ==========
main() {
    echo "=========================================="
    echo "  Passport Web + Certificate Pipeline"
    echo "  全自动安装脚本"
    echo "=========================================="
    echo
    
    check_root
    check_system
    setup_mirrors
    
    log_info "开始安装..."
    
    install_system_deps
    create_dirs
    create_venv
    install_python_deps
    download_models
    deploy_webapp
    verify_install
    create_service
    start_service
    health_check
    show_summary
}

# 运行主流程
main "$@"