#!/bin/bash
###############################################################################
# NGINX Installation Script for China Mainland with Mirror Acceleration
###############################################################################
# @author    Advanced Bash Shell Engineer
# @version   1.0.0
# @license   MIT
# @created   2026-01-19
# @desc      Production-grade NGINX installation script with China mirror support
#            Supports Ubuntu 18.04/20.04/22.04/24.04 with version pinning
###############################################################################

###############################################################################
# GLOBAL CONSTANTS
###############################################################################
readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"
readonly SCRIPT_VERSION="1.0.0"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Color codes for output
readonly COLOR_RED="\033[0;31m"
readonly COLOR_GREEN="\033[0;32m"
readonly COLOR_YELLOW="\033[1;33m"
readonly COLOR_BLUE="\033[0;34m"
readonly COLOR_RESET="\033[0m"

# Log levels
readonly LOG_LEVEL_DEBUG=0
readonly LOG_LEVEL_INFO=1
readonly LOG_LEVEL_WARN=2
readonly LOG_LEVEL_ERROR=3

# Default configuration
readonly DEFAULT_NGINX_VERSION="stable"
readonly DEFAULT_MIRROR="ustc"
readonly SUPPORTED_UBUNTU_VERSIONS=("18.04" "20.04" "22.04" "24.04")

# Mirror configurations (China mainland accelerated sources)
declare -A MIRROR_URLS=(
    ["aliyun"]="http://mirrors.aliyun.com/nginx"
    ["tsinghua"]="https://mirrors.tuna.tsinghua.edu.cn/nginx"
        ["ustc"]="https://mirrors.ustc.edu.cn/nginx/ubuntu"
    ["official"]="http://nginx.org"
)

declare -A MIRROR_KEY_URLS=(
    ["aliyun"]="http://mirrors.aliyun.com/nginx/keys/nginx_signing.key"
    ["tsinghua"]="https://mirrors.tuna.tsinghua.edu.cn/nginx/keys/nginx_signing.key"
    ["ustc"]="https://mirrors.ustc.edu.cn/nginx/keys/nginx_signing.key"
    ["official"]="https://nginx.org/keys/nginx_signing.key"
)

# Global variables
CURRENT_LOG_LEVEL="${LOG_LEVEL_INFO}"
NGINX_VERSION="${DEFAULT_NGINX_VERSION}"
MIRROR_SOURCE="${DEFAULT_MIRROR}"
FORCE_REINSTALL=false
DRY_RUN=false

###############################################################################
# ERROR HANDLING & TRAPS
###############################################################################
set -euo pipefail
IFS=$'\n\t'

###
### Cleanup function for graceful exit
### @param none
### @return void
### @require none
###
cleanup() {
    local exit_code=$?
    if [[ ${exit_code} -ne 0 ]]; then
        log_error "脚本退出，错误码： ${exit_code}"
    fi
    # > Perform cleanup operations if needed
    return "${exit_code}"
}

trap cleanup EXIT
trap 'log_error "用户中断脚本执行"; exit 130' INT TERM

###############################################################################
# LOGGING FUNCTIONS
###############################################################################

###
### Core logging function with level-based filtering
### @param log_level integer Log level (0-3)
### @param message string Message to log
### @param color string Color code for output
### @return void
### @require none
###
_log() {
    local log_level=$1
    local message=$2
    local color=$3
    local level_name=$4

    if [[ ${log_level} -ge ${CURRENT_LOG_LEVEL} ]]; then
        local timestamp
        timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
        echo -e "${color}[${timestamp}] [${level_name}] ${message}${COLOR_RESET}" >&2
    fi
}

###
### Debug level logging
### @param message string Debug message
### @return void
### @require none
###
log_debug() {
    _log "${LOG_LEVEL_DEBUG}" "$1" "${COLOR_BLUE}" "调试"
}

###
### Info level logging
### @param message string Info message
### @return void
### @require none
###
log_info() {
    _log "${LOG_LEVEL_INFO}" "$1" "${COLOR_GREEN}" "信息"
}

###
### Warning level logging
### @param message string Warning message
### @return void
### @require none
###
log_warn() {
    _log "${LOG_LEVEL_WARN}" "$1" "${COLOR_YELLOW}" "警告"
}

###
### Error level logging
### @param message string Error message
### @return void
### @require none
###
log_error() {
    _log "${LOG_LEVEL_ERROR}" "$1" "${COLOR_RED}" "错误"
}

###############################################################################
# VALIDATION FUNCTIONS
###############################################################################

###
### Check if script is running with root privileges
### @param none
### @return 0 if root, 1 otherwise
### @require none
###
check_root_privileges() {
    if [[ ${EUID} -ne 0 ]]; then
        log_error "此脚本必须以 root 身份运行，或使用 sudo 执行"
        return 1
    fi
    log_debug "已确认具备 root 权限"
    return 0
}

###
### Validate Ubuntu version compatibility
### @param none
### @return 0 if supported, 1 otherwise
### @require lsb_release command
###
validate_ubuntu_version() {
    local ubuntu_version

    # > Check if lsb_release exists
    if ! command -v lsb_release &> /dev/null; then
        log_error "未找到 lsb_release 命令，无法识别 Ubuntu 版本。"
        return 1
    fi

    ubuntu_version="$(lsb_release -rs)"
    log_debug "检测到 Ubuntu 版本： ${ubuntu_version}"

    # > Validate against supported versions
    local supported=false
    for version in "${SUPPORTED_UBUNTU_VERSIONS[@]}"; do
        if [[ "${ubuntu_version}" == "${version}" ]]; then
            supported=true
            break
        fi
    done

    if [[ "${supported}" == false ]]; then
        log_error "Ubuntu ${ubuntu_version} 不受支持。支持的版本： ${SUPPORTED_UBUNTU_VERSIONS[*]}"
        return 1
    fi

    log_info "Ubuntu ${ubuntu_version} 受支持"
    return 0
}

###
### Validate mirror source selection
### @param mirror_name string 镜像源 name
### @return 0 if valid, 1 otherwise
### @require none
###
validate_mirror_source() {
    local mirror_name=$1

    if [[ ! -v MIRROR_URLS["${mirror_name}"] ]]; then
        log_error "无效的镜像源： ${mirror_name}"
        log_info "可用镜像源： ${!MIRROR_URLS[*]}"
        return 1
    fi

    log_debug "镜像源 '${mirror_name}' 有效"
    return 0
}

###
### Check network connectivity to mirror
### @param mirror_url string URL to test
### @return 0 if reachable, 1 otherwise
### @require curl
###
check_mirror_connectivity() {
    local mirror_url=$1
    local timeout=10

    log_debug "正在测试镜像连通性： ${mirror_url}"

    if curl -sSf --connect-timeout "${timeout}" --max-time "${timeout}" \
        "${mirror_url}" -o /dev/null 2>/dev/null; then
        log_debug "镜像 ${mirror_url} 可访问"
        return 0
    else
        log_warn "镜像 ${mirror_url} 不可访问"
        return 1
    fi
}

###############################################################################
# SYSTEM PREPARATION FUNCTIONS
###############################################################################

###
### Install required system dependencies
### @param none
### @return 0 on success, 1 on failure
### @require apt-get
###
install_dependencies() {
    log_info "正在安装系统依赖..."

    local dependencies=(
        "curl"
        "gnupg2"
        "ca-certificates"
        "lsb-release"
        "ubuntu-keyring"
        "apt-transport-https"
    )

    if [[ "${DRY_RUN}" == true ]]; then
        log_info "[演练模式] 将会安装： ${dependencies[*]}"
        return 0
    fi

    # > Update package index first
    if ! apt-get update -qq; then
        log_error "更新软件包索引失败"
        return 1
    fi

    # > Install dependencies
    if ! apt-get install -y -qq "${dependencies[@]}"; then
        log_error "安装依赖失败"
        return 1
    fi

    log_info "依赖安装完成"
    return 0
}

###
### Remove existing NGINX installation if present
### @param none
### @return 0 on success or if not installed
### @require apt-get, dpkg
###
remove_existing_nginx() {
    log_info "正在检查是否已安装 NGINX..."

    if ! dpkg -l | grep -q "^ii.*nginx"; then
        log_info "未发现已安装的 NGINX"
        return 0
    fi

    if [[ "${FORCE_REINSTALL}" == false ]]; then
        log_warn "NGINX 已安装。如需重装请使用 --force。"
        return 1
    fi

    log_info "正在卸载已安装的 NGINX..."

    if [[ "${DRY_RUN}" == true ]]; then
        log_info "[演练模式] 将会卸载已安装的 NGINX"
        return 0
    fi

    # > Stop NGINX service if running
    if systemctl is-active --quiet nginx 2>/dev/null; then
        systemctl stop nginx || true
    fi

    # > Remove NGINX packages
    if ! apt-get remove --purge -y nginx nginx-common nginx-full 2>/dev/null; then
        log_warn "部分 NGINX 软件包可能未能完全卸载（可忽略）"
    fi

    # > Clean up configuration files
    apt-get autoremove -y -qq || true

    log_info "已卸载现有 NGINX"
    return 0
}

###############################################################################
# NGINX INSTALLATION FUNCTIONS
###############################################################################

###
### Import NGINX GPG signing key
### @param mirror_name string 镜像源 name
### @return 0 on success, 1 on failure
### @require curl, gpg
###
import_nginx_gpg_key() {
    local mirror_name=$1
    local key_url="${MIRROR_KEY_URLS[${mirror_name}]}"
    local keyring_path="/usr/share/keyrings/nginx-archive-keyring.gpg"

    log_info "正在导入 NGINX GPG 签名密钥（来源：${mirror_name}）..."

    if [[ "${DRY_RUN}" == true ]]; then
        log_info "[演练模式] 将会从以下地址导入 GPG 密钥： ${key_url}"
        return 0
    fi

    # > Remove old keyring if exists
    [[ -f "${keyring_path}" ]] && rm -f "${keyring_path}"

    # > Download and import GPG key
    if ! curl -fsSL "${key_url}" | gpg --dearmor -o "${keyring_path}" 2>/dev/null; then
        log_error "导入 GPG 密钥失败： ${key_url}"
        return 1
    fi

    # > Verify the key was imported correctly
    if ! gpg --dry-run --quiet --no-keyring --import --import-options import-show \
        "${keyring_path}" &>/dev/null; then
        log_error "GPG 密钥校验失败"
        return 1
    fi

    # > Set proper permissions
    chmod 644 "${keyring_path}"

    log_info "GPG 密钥导入并校验成功"
    return 0
}

###
### Configure NGINX APT repository
### @param mirror_name string 镜像源 name
### @return 0 on success, 1 on failure
### @require lsb_release
###
configure_nginx_repository() {
    local mirror_name=$1
    local mirror_url="${MIRROR_URLS[${mirror_name}]}"
    local codename
    codename="$(lsb_release -cs)"
    local repo_file="/etc/apt/sources.list.d/nginx.list"
    local keyring_path="/usr/share/keyrings/nginx-archive-keyring.gpg"

# > 不同镜像源目录结构可能不同：
# > - 官方/部分镜像：.../packages/ubuntu
# > - USTC：.../ubuntu
local repo_base
case "${mirror_name}" in
    ustc)
        repo_base="${mirror_url}"
        ;;
    *)
        repo_base="${mirror_url}/packages/ubuntu"
        ;;
esac

    log_info "正在配置 NGINX 软件源（Ubuntu ${codename}）..."

    if [[ "${DRY_RUN}" == true ]]; then
        log_info "[演练模式] 将会配置软件源：deb [signed-by=${keyring_path}] ${repo_base} ${codename} nginx"
        return 0
    fi

    # > Create repository configuration
    local repo_config="deb [signed-by=${keyring_path}] ${repo_base} ${codename} nginx"
    echo "${repo_config}" | tee "${repo_file}" > /dev/null

    log_debug "已生成软件源配置文件：${repo_file}"
    log_debug "软件源地址：${repo_base} ${codename}"

    log_info "NGINX 软件源配置完成"
    return 0
}

###
### Configure APT pinning preferences for NGINX
### @param none
### @return 0 on success
### @require none
###
configure_apt_pinning() {
    local pref_file="/etc/apt/preferences.d/99nginx"

    log_info "正在配置 APT Pin 优先级..."

    if [[ "${DRY_RUN}" == true ]]; then
        log_info "[演练模式] 将会配置 APT Pin 优先级"
        return 0
    fi

    # > Create pinning configuration for priority
    cat > "${pref_file}" <<EOF
Package: *
Pin: origin nginx.org
Pin: release o=nginx
Pin-Priority: 900
EOF

    log_debug "APT Pin 配置写入：${pref_file}"
    log_info "APT Pin 优先级配置完成"
    return 0
}

###
### Install NGINX package
### @param version string NGINX version to install (stable/mainline/specific)
### @return 0 on success, 1 on failure
### @require apt-get
###
install_nginx_package() {
    local version=$1
    local package_spec="nginx"

    log_info "正在安装 NGINX ${version}..."

    # > Update package index with new repository
    if [[ "${DRY_RUN}" == false ]]; then
        if ! apt-get update -qq; then
            log_error "更新软件包索引失败"
            return 1
        fi
    fi

    # > Handle version specification
    if [[ "${version}" != "stable" && "${version}" != "mainline" ]]; then
        # > Specific version requested
        package_spec="nginx=${version}"
        log_debug "安装指定版本：${package_spec}"
    else
        log_debug "从软件源安装：${version}"
    fi

    if [[ "${DRY_RUN}" == true ]]; then
        log_info "[演练模式] 将会安装软件包：${package_spec}"
        return 0
    fi

    # > Install NGINX
    if ! DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "${package_spec}"; then
        log_error "安装 NGINX 失败"
        return 1
    fi

    log_info "NGINX 安装完成"
    return 0
}

###
### Verify NGINX installation
### @param none
### @return 0 on success, 1 on failure
### @require nginx
###
verify_nginx_installation() {
    log_info "正在验证 NGINX 安装结果..."

    # > Check if nginx binary exists
    if ! command -v nginx &> /dev/null; then
        log_error "未在 PATH 中找到 nginx 可执行文件"
        return 1
    fi

    # > Get and display version
    local nginx_version_output
    nginx_version_output="$(nginx -v 2>&1)"
    log_info "已安装： ${nginx_version_output}"

    # > Test configuration
    if ! nginx -t &>/dev/null; then
        log_error "NGINX 配置文件校验失败"
        return 1
    fi

    log_info "NGINX 安装验证通过"
    return 0
}

###
### Enable and start NGINX service
### @param none
### @return 0 on success, 1 on failure
### @require systemctl
###
enable_nginx_service() {
    log_info "正在设置 NGINX 开机自启并启动服务..."

    if [[ "${DRY_RUN}" == true ]]; then
        log_info "[演练模式] 将会启用并启动 NGINX 服务"
        return 0
    fi

    # > Enable service to start on boot
    if ! systemctl enable nginx &>/dev/null; then
        log_error "设置 NGINX 开机自启失败"
        return 1
    fi

    # > Start the service
    if ! systemctl start nginx; then
        log_error "启动 NGINX 服务失败"
        return 1
    fi

    # > Verify service is running
    if ! systemctl is-active --quiet nginx; then
        log_error "NGINX 服务未处于运行状态"
        return 1
    fi

    log_info "NGINX 服务已启用并启动"
    return 0
}

###############################################################################
# MAIN ORCHESTRATION
###############################################################################

###
### Display usage information
### @param none
### @return void
### @require none
###
show_usage() {
    cat <<EOF
Usage: ${SCRIPT_NAME} [选项]

NGINX 安装脚本（面向中国大陆镜像加速） v${SCRIPT_VERSION}

选项：
    -v, --version VERSION       指定要安装的 NGINX 版本
                                （stable/mainline/1.24.0/...）
                                默认：${DEFAULT_NGINX_VERSION}

    -m, --mirror MIRROR         选择镜像源
                                （aliyun/tsinghua/ustc/official）
                                默认：${DEFAULT_MIRROR}

    -f, --force                 若已安装则强制重装

    -d, --dry-run               演练模式：仅展示将执行的操作，不真正执行

    --debug                     开启调试日志

    -h, --help                  显示帮助信息

示例：
    # 使用默认镜像（USTC）安装稳定版
    sudo ${SCRIPT_NAME}

    # 使用清华镜像安装指定版本
    sudo ${SCRIPT_NAME} --version 1.24.0 --mirror tsinghua

    # 强制重装并开启调试
    sudo ${SCRIPT_NAME} --force --debug

    # 演练模式预览
    sudo ${SCRIPT_NAME} --dry-run

支持的 Ubuntu 版本：
    ${SUPPORTED_UBUNTU_VERSIONS[*]}

可用镜像源：
    ${!MIRROR_URLS[*]}
EOF
}

###
### Parse command line arguments
### @param args array Command line arguments
### @return 0 on success, 1 on invalid arguments
### @require none
###
parse_arguments() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -v|--version)
                NGINX_VERSION="$2"
                shift 2
                ;;
            -m|--mirror)
                MIRROR_SOURCE="$2"
                shift 2
                ;;
            -f|--force)
                FORCE_REINSTALL=true
                shift
                ;;
            -d|--dry-run)
                DRY_RUN=true
                shift
                ;;
            --debug)
                CURRENT_LOG_LEVEL="${LOG_LEVEL_DEBUG}"
                shift
                ;;
            -h|--help)
                show_usage
                exit 0
                ;;
            *)
                log_error "未知参数： $1"
                show_usage
                exit 1
                ;;
        esac
    done

    return 0
}

###
### Main installation workflow
### @param none
### @return 0 on success, 1 on failure
### @require all functions above
###
main() {
    log_info "========================================="
    log_info "NGINX 安装脚本 v${SCRIPT_VERSION}"
    log_info "========================================="

    # > Step 1: Pre-flight checks
    log_info "步骤 1/8：执行预检查..."
    check_root_privileges || return 1
    validate_ubuntu_version || return 1
    validate_mirror_source "${MIRROR_SOURCE}" || return 1

    # > Step 2: Check mirror connectivity
    log_info "步骤 2/8：检查镜像连通性..."
    if ! check_mirror_connectivity "${MIRROR_URLS[${MIRROR_SOURCE}]}"; then
        log_warn "主镜像不可用，尝试回退方案..."
        # > Fallback to official if mirror fails
        if [[ "${MIRROR_SOURCE}" != "official" ]]; then
            MIRROR_SOURCE="official"
            log_info "已回退到官方源"
        fi
    fi

    # > Step 3: Install dependencies
    log_info "步骤 3/8：安装依赖..."
    install_dependencies || return 1

    # > Step 4: Handle existing installation
    log_info "步骤 4/8：检查已安装版本..."
    remove_existing_nginx || return 1

    # > Step 5: Import GPG key
    log_info "步骤 5/8：导入 NGINX GPG 密钥..."
    import_nginx_gpg_key "${MIRROR_SOURCE}" || return 1

    # > Step 6: Configure repository
    log_info "步骤 6/8：配置 NGINX 软件源..."
    configure_nginx_repository "${MIRROR_SOURCE}" || return 1
    configure_apt_pinning || return 1

    # > Step 7: Install NGINX
    log_info "步骤 7/8：安装 NGINX..."
    install_nginx_package "${NGINX_VERSION}" || return 1
    verify_nginx_installation || return 1

    # > Step 8: Enable service
    log_info "步骤 8/8：启用 NGINX 服务..."
    enable_nginx_service || return 1

    log_info "========================================="
    log_info "✓ NGINX 安装完成！"
    log_info "========================================="

    if [[ "${DRY_RUN}" == false ]]; then
        log_info "服务状态： $(systemctl is-active nginx)"
        log_info "NGINX 版本： $(nginx -v 2>&1 | cut -d'/' -f2)"
        log_info ""
        log_info "常用命令："
        log_info "  启动：   sudo systemctl start nginx"
        log_info "  停止：    sudo systemctl stop nginx"
        log_info "  重启： sudo systemctl restart nginx"
        log_info "  状态：  sudo systemctl status nginx"
        log_info "  校验配置：    sudo nginx -t"
    fi

    return 0
}

###############################################################################
# SCRIPT ENTRY POINT
###############################################################################

# ASCII Flow Diagram - Function Call Hierarchy
# ┌─────────────────────────────────────────────────────────────┐
# │                         MAIN()                              │
# └──────────────────┬──────────────────────────────────────────┘
#                    │
#      ┌─────────────┼─────────────┬──────────────┬─────────────┐
#      │             │             │              │             │
#      ▼             ▼             ▼              ▼             ▼
# ┌─────────┐  ┌──────────┐  ┌─────────┐  ┌────────────┐  ┌─────────┐
# │Pre-flight│ │Install   │  │Import   │  │Configure   │  │Install  │
# │Checks    │ │Deps      │  │GPG Key  │  │Repository  │  │NGINX    │
# └─────────┘  └──────────┘  └─────────┘  └────────────┘  └─────────┘
#      │             │             │              │             │
#      ├─check_root_privileges     │              │             │
#      ├─validate_ubuntu_version   │              │             │
#      └─validate_mirror_source    │              │             │
#                    │             │              │             │
#                    └─install_dependencies       │             │
#                                  │              │             │
#                                  └─import_nginx_gpg_key      │
#                                                 │             │
#                                                 ├─configure_nginx_repository
#                                                 └─configure_apt_pinning
#                                                               │
#                                                               ├─install_nginx_package
#                                                               └─verify_nginx_installation

# Parse command line arguments
parse_arguments "$@"

# Execute main workflow
main
exit $?
