#!/bin/bash
# Caddy + Fail2ban 自动安装脚本 (增强版)
# 结合了多个版本的优点，并增加了健壮性和用户友好性。
# 版本: 带多语言支持

# --- 脚本行为设置 ---
set -e
set -o pipefail

# --- 全局语言设置 ---
SCRIPT_LANG="zh" # 默认为中文

# --- 颜色定义 (仅当输出到 TTY 时) ---
if [ -t 1 ]; then
    RED='\033[0;31m'
    GREEN='\033[0;32m'
    YELLOW='\033[1;33m'
    BLUE='\033[0;34m'
    NC='\033[0m' # No Color
else
    RED=''
    GREEN=''
    YELLOW=''
    BLUE=''
    NC=''
fi

# --- 文本资源 ---
declare -A TEXTS

# General & Language Selection
TEXTS[select_lang_prompt_bilingual]="请选择语言 / Please select language:"
TEXTS[select_lang_option_zh_bilingual]="  1. 中文 (Chinese)"
TEXTS[select_lang_option_en_bilingual]="  2. English"
TEXTS[select_lang_input_prompt_bilingual]="输入选项 / Enter option (1/2) [Default: 1. 中文 / Chinese]: "
TEXTS[lang_set_to_chinese_zh]="语言已设置为中文。"
TEXTS[lang_set_to_english_en]="Language set to English."

# Log Levels
TEXTS[log_level_info_zh]="信息"
TEXTS[log_level_info_en]="INFO"
TEXTS[log_level_warn_zh]="警告"
TEXTS[log_level_warn_en]="WARN"
TEXTS[log_level_error_zh]="错误"
TEXTS[log_level_error_en]="ERROR"

# detect_user_ids_and_engine
TEXTS[warn_running_as_root_zh]="脚本正以 root 用户身份运行。"
TEXTS[warn_running_as_root_en]="Script is running as root."
TEXTS[warn_root_puid_pgid_zh]="为安全起见，容器内的服务将尝试以 PUID=1000, PGID=1000 运行。"
TEXTS[warn_root_puid_pgid_en]="For security reasons, services inside containers will attempt to run with PUID=1000, PGID=1000."
TEXTS[info_running_as_user_zh]="脚本以普通用户 %s 身份运行。"
TEXTS[info_running_as_user_en]="Script is running as user %s."
TEXTS[info_container_puid_pgid_zh]="容器服务将使用的 PUID: ${BLUE}%s${NC}, PGID: ${BLUE}%s${NC}"
TEXTS[info_container_puid_pgid_en]="Container services will use PUID: ${BLUE}%s${NC}, PGID: ${BLUE}%s${NC}"
TEXTS[warn_old_docker_compose_zh]="检测到旧版 docker-compose (v1)。建议升级到 Docker Compose v2 ('docker compose')。👍"
TEXTS[warn_old_docker_compose_en]="Detected legacy docker-compose (v1). It is recommended to upgrade to Docker Compose v2 ('docker compose'). 👍"
TEXTS[error_docker_compose_missing_zh]="Docker 已安装, 但 'docker compose' (v2) 或 'docker-compose' (v1) 未找到。"
TEXTS[error_docker_compose_missing_en]="Docker is installed, but 'docker compose' (v2) or 'docker-compose' (v1) was not found."
TEXTS[warn_podman_compose_missing_zh]="Podman 已安装, 但 'podman-compose' 未找到。您可能需要手动管理。"
TEXTS[warn_podman_compose_missing_en]="Podman is installed, but 'podman-compose' was not found. You may need to manage services manually."
TEXTS[error_no_container_engine_zh]="未找到 Docker 或 Podman。请先安装。😥"
TEXTS[error_no_container_engine_en]="Neither Docker nor Podman found. Please install one first. 😥"
TEXTS[info_using_container_engine_zh]="将使用容器引擎: ${BLUE}%s${NC}"
TEXTS[info_using_container_engine_en]="Will use container engine: ${BLUE}%s${NC}"
TEXTS[info_using_compose_command_zh]="将使用 Compose 命令: ${BLUE}%s${NC}"
TEXTS[info_using_compose_command_en]="Will use Compose command: ${BLUE}%s${NC}"
TEXTS[warn_no_compose_command_zh]="未找到兼容的 Compose 命令。您可能需要手动管理容器生命周期。"
TEXTS[warn_no_compose_command_en]="No compatible Compose command found. You may need to manage container lifecycle manually."

# main_install
TEXTS[info_begin_install_zh]="🚀 开始安装 Caddy 和 Fail2Ban 服务..."
TEXTS[info_begin_install_en]="🚀 Starting Caddy and Fail2Ban service installation..."
TEXTS[prompt_install_dir_zh]="请输入安装目录 (默认: %s): "
TEXTS[prompt_install_dir_en]="Enter installation directory (default: %s): "
TEXTS[info_install_dir_set_zh]="安装目录将设置为: ${BLUE}%s${NC}"
TEXTS[info_install_dir_set_en]="Installation directory will be set to: ${BLUE}%s${NC}"
TEXTS[warn_no_write_permission_zh]="当前用户 %s 可能没有 %s 目录的写入权限来创建 %s。"
TEXTS[warn_no_write_permission_en]="Current user %s may not have write permission to directory %s to create %s."
TEXTS[warn_permission_suggestion_zh]="请考虑：\n  1. 使用 sudo 运行此脚本。\n  2. 确保您有权限在 %s 下创建目录。"
TEXTS[warn_permission_suggestion_en]="Please consider:\n  1. Running this script with sudo.\n  2. Ensuring you have permission to create directories under %s."
TEXTS[prompt_continue_anyway_zh]="是否继续尝试创建目录 (可能失败或需要sudo权限)? (y/N): "
TEXTS[prompt_continue_anyway_en]="Continue trying to create directory (may fail or require sudo)? (y/N): "
TEXTS[info_install_cancelled_zh]="安装已取消。下次再见！😊"
TEXTS[info_install_cancelled_en]="Installation cancelled. See you next time! 😊"
TEXTS[info_creating_files_in_zh]="将在以下位置创建文件和目录: ${BLUE}%s${NC}"
TEXTS[info_creating_files_in_en]="Files and directories will be created at: ${BLUE}%s${NC}"
TEXTS[info_dirs_created_zh]="✅ 目录结构创建完成。"
TEXTS[info_dirs_created_en]="✅ Directory structure created."
TEXTS[info_creating_file_zh]="📝 创建 %s..."
TEXTS[info_creating_file_en]="📝 Creating %s..."
TEXTS[info_file_created_zh]="✅ %s 创建完成。"
TEXTS[info_file_created_en]="✅ %s created."
TEXTS[compose_tz_comment_zh]="# 请根据您的时区修改"
TEXTS[compose_tz_comment_en]="# Please change to your timezone"
TEXTS[info_caddyfile_http_port_zh]="💡 ${YELLOW}提示:${NC} Caddyfile 中的 HTTP 服务监听在 80 端口。如需 HTTPS 或反向代理，请编辑 ${BLUE}%s${NC}。"
TEXTS[info_caddyfile_http_port_en]="💡 ${YELLOW}Hint:${NC} The HTTP service in Caddyfile listens on port 80. For HTTPS or reverse proxy, please edit ${BLUE}%s${NC}."
TEXTS[info_fail2ban_filters_generated_zh]="💡 ${YELLOW}提示:${NC} Fail2Ban filter 规则已生成。您可以在 ${BLUE}%s${NC} 中根据需要调整它们。"
TEXTS[info_fail2ban_filters_generated_en]="💡 ${YELLOW}Hint:${NC} Fail2Ban filter rules have been generated. You can adjust them in ${BLUE}%s${NC} as needed."
TEXTS[info_configuring_bash_env_zh]="⚙️  配置 Bash 环境 (添加 'caddy_helper' 和 'fail2ban_helper' 管理命令)..."
TEXTS[info_configuring_bash_env_en]="⚙️  Configuring Bash environment (adding 'caddy_helper' and 'fail2ban_helper' management commands)..."
TEXTS[info_bashrc_source_added_zh]="✅ 已将 source %s 添加到 ${BLUE}%s${NC}"
TEXTS[info_bashrc_source_added_en]="✅ Added source %s to ${BLUE}%s${NC}"
TEXTS[info_bashrc_source_exists_zh]="ℹ️  ${BLUE}%s${NC} 中已存在 source %s"
TEXTS[info_bashrc_source_exists_en]="ℹ️  Source %s already exists in ${BLUE}%s${NC}"
TEXTS[warn_bashrc_not_found_zh]="未找到 ${BLUE}%s${NC}。请手动添加以下行到您的 shell 配置文件中 (如 .zshrc, .profile 等):"
TEXTS[warn_bashrc_not_found_en]="File ${BLUE}%s${NC} not found. Please manually add the following line to your shell configuration file (e.g., .zshrc, .profile, etc.):"
TEXTS[info_helper_script_created_zh]="✅ Bash 助手脚本 ${BLUE}%s${NC} 创建完成。"
TEXTS[info_helper_script_created_en]="✅ Bash helper script ${BLUE}%s${NC} created."
TEXTS[info_run_source_bashrc_zh]="💡 请运行 ${YELLOW}'source ~/.bashrc'${NC} 或重新打开终端使 'caddy_helper' 和 'fail2ban_helper' 命令及其自动补全生效。"
TEXTS[info_run_source_bashrc_en]="💡 Please run ${YELLOW}'source ~/.bashrc'${NC} or reopen your terminal for 'caddy_helper' and 'fail2ban_helper' commands and their autocompletion to take effect."
TEXTS[info_install_complete_zh]="🎉 ${GREEN}安装完成！一切准备就绪。${NC}"
TEXTS[info_install_complete_en]="🎉 ${GREEN}Installation complete! Everything is ready.${NC}"
TEXTS[info_service_directory_zh]="服务目录: ${BLUE}%s${NC}"
TEXTS[info_service_directory_en]="Service directory: ${BLUE}%s${NC}"
TEXTS[info_start_service_zh]="➡️  启动服务: ${YELLOW}cd \"%s\" && %s up -d${NC}"
TEXTS[info_start_service_en]="➡️  Start services: ${YELLOW}cd \"%s\" && %s up -d${NC}"
TEXTS[info_stop_service_zh]="⬅️  停止服务: ${YELLOW}cd \"%s\" && %s down${NC}"
TEXTS[info_stop_service_en]="⬅️  Stop services: ${YELLOW}cd \"%s\" && %s down${NC}"
TEXTS[info_view_caddy_logs_zh]="👀 查看Caddy日志: ${YELLOW}caddy_helper logs${NC}"
TEXTS[info_view_caddy_logs_en]="👀 View Caddy logs: ${YELLOW}caddy_helper logs${NC}"
TEXTS[info_view_f2b_logs_zh]="👀 查看Fail2Ban日志: ${YELLOW}fail2ban_helper logs${NC}"
TEXTS[info_view_f2b_logs_en]="👀 View Fail2Ban logs: ${YELLOW}fail2ban_helper logs${NC}"
TEXTS[info_test_f2b_rules_zh]="🧪 测试Fail2Ban规则 (服务启动后): ${YELLOW}fail2ban_helper test_rules${NC}"
TEXTS[info_test_f2b_rules_en]="🧪 Test Fail2Ban rules (after services start): ${YELLOW}fail2ban_helper test_rules${NC}"
TEXTS[warn_manual_container_management_zh]="由于未找到兼容的 Compose 命令，您需要手动管理容器。"
TEXTS[warn_manual_container_management_en]="As no compatible Compose command was found, you'll need to manage containers manually."
TEXTS[info_helper_commands_added_zh]="💡 助手命令已添加 (需重新 source ~/.bashrc 或重开终端):"
TEXTS[info_helper_commands_added_en]="💡 Helper commands added (need to re-source ~/.bashrc or reopen terminal):"
TEXTS[info_caddy_helper_help_zh]="  ${YELLOW}caddy_helper help${NC}    - 查看 Caddy 管理命令帮助"
TEXTS[info_caddy_helper_help_en]="  ${YELLOW}caddy_helper help${NC}    - Show Caddy management command help"
TEXTS[info_f2b_helper_help_zh]="  ${YELLOW}fail2ban_helper help${NC} - 查看 Fail2Ban 管理命令帮助"
TEXTS[info_f2b_helper_help_en]="  ${YELLOW}fail2ban_helper help${NC} - Show Fail2Ban management command help"

# uninstall_services
TEXTS[info_begin_uninstall_zh]="🚧 开始卸载 Caddy 和 Fail2Ban 服务..."
TEXTS[info_begin_uninstall_en]="🚧 Starting uninstallation of Caddy and Fail2Ban services..."
TEXTS[warn_cannot_determine_install_dir_zh]="无法从 helper 脚本中确定安装目录。将尝试使用默认路径: %s"
TEXTS[warn_cannot_determine_install_dir_en]="Cannot determine installation directory from helper script. Will try default path: %s"
TEXTS[info_install_dir_from_helper_zh]="从 helper 脚本中获取到安装目录: ${BLUE}%s${NC}"
TEXTS[info_install_dir_from_helper_en]="Obtained installation directory from helper script: ${BLUE}%s${NC}"
TEXTS[warn_helper_script_not_found_uninstall_zh]="Helper 脚本未找到。将尝试使用默认路径: %s 进行卸载。"
TEXTS[warn_helper_script_not_found_uninstall_en]="Helper script not found. Will try using default path: %s for uninstallation."
TEXTS[info_stopping_containers_zh]="🛑 停止并移除容器 (包括卷)..."
TEXTS[info_stopping_containers_en]="🛑 Stopping and removing containers (including volumes)..."
TEXTS[warn_stop_container_failed_zh]="⚠️  停止容器失败，可能服务未运行或已被移除。"
TEXTS[warn_stop_container_failed_en]="⚠️  Failed to stop containers, services might not be running or already removed."
TEXTS[prompt_delete_config_data_zh]="是否要删除所有配置文件和数据目录 ${BLUE}%s${NC}? [y/N]: "
TEXTS[prompt_delete_config_data_en]="Delete all configuration files and data directory ${BLUE}%s${NC}? [y/N]: "
TEXTS[info_deleting_dir_zh]="🗑️  正在删除 %s..."
TEXTS[info_deleting_dir_en]="🗑️  Deleting %s..."
TEXTS[info_dir_deleted_zh]="✅ %s 已删除。"
TEXTS[info_dir_deleted_en]="✅ %s deleted."
TEXTS[info_keep_dir_zh]="👍 保留目录 %s。"
TEXTS[info_keep_dir_en]="👍 Directory %s kept."
TEXTS[info_install_dir_not_found_zh]="ℹ️  安装目录 ${BLUE}%s${NC} 未找到，无需删除。"
TEXTS[info_install_dir_not_found_en]="ℹ️  Installation directory ${BLUE}%s${NC} not found, no need to delete."
TEXTS[info_deleting_helper_script_zh]="🗑️  正在删除 Bash 助手脚本 ${BLUE}%s${NC}..."
TEXTS[info_deleting_helper_script_en]="🗑️  Deleting Bash helper script ${BLUE}%s${NC}..."
TEXTS[info_helper_script_deleted_zh]="✅ Bash 助手已删除。"
TEXTS[info_helper_script_deleted_en]="✅ Bash helper deleted."
TEXTS[info_removing_bashrc_ref_zh]="🧹 正在从 ${BLUE}%s${NC} 中移除对助手脚本的引用..."
TEXTS[info_removing_bashrc_ref_en]="🧹 Removing reference to helper script from ${BLUE}%s${NC}..."
TEXTS[info_bashrc_ref_removed_zh]="✅ 已从 %s 中移除引用。备份文件为 ${BLUE}%s.bak${NC}"
TEXTS[info_bashrc_ref_removed_en]="✅ Reference removed from %s. Backup file is ${BLUE}%s.bak${NC}"
TEXTS[info_uninstall_complete_zh]="🎉 ${GREEN}卸载完成。江湖再见！${NC} 👋"
TEXTS[info_uninstall_complete_en]="🎉 ${GREEN}Uninstallation complete. Farewell!${NC} 👋"

# main script help
TEXTS[main_help_title_zh]="${BLUE}Caddy + Fail2Ban 自动安装与管理脚本 (增强版)${NC}"
TEXTS[main_help_title_en]="${BLUE}Caddy + Fail2Ban Automatic Installation & Management Script (Enhanced)${NC}"
TEXTS[main_help_usage_zh]="${YELLOW}用法:${NC} ./%s [命令]"
TEXTS[main_help_usage_en]="${YELLOW}Usage:${NC} ./%s [command]"
TEXTS[main_help_available_commands_zh]="${BLUE}可用命令:${NC}"
TEXTS[main_help_available_commands_en]="${BLUE}Available commands:${NC}"
TEXTS[main_help_cmd_default_zh]="  (无命令)    - 执行标准安装流程 (推荐)。"
TEXTS[main_help_cmd_default_en]="  (no command)  - Perform standard installation (recommended)."
TEXTS[main_help_cmd_uninstall_zh]="  uninstall   - 尝试停止服务并删除所有相关文件、配置和 Bash 助手。"
TEXTS[main_help_cmd_uninstall_en]="  uninstall   - Attempt to stop services and remove all related files, configs, and Bash helper."
TEXTS[main_help_cmd_help_zh]="  help        - 显示此帮助信息。"
TEXTS[main_help_cmd_help_en]="  help        - Display this help message."
TEXTS[main_help_default_install_dir_zh]="默认安装目录: ${BLUE}%s${NC} (可在安装时自定义)"
TEXTS[main_help_default_install_dir_en]="Default installation directory: ${BLUE}%s${NC} (customizable during installation)"
TEXTS[main_help_source_bashrc_zh]="安装后，请 ${YELLOW}source ~/.bashrc${NC} 或重开终端以使用 ${GREEN}caddy_helper${NC} 和 ${GREEN}fail2ban_helper${NC} 命令。"
TEXTS[main_help_source_bashrc_en]="After installation, please ${YELLOW}source ~/.bashrc${NC} or reopen terminal to use ${GREEN}caddy_helper${NC} and ${GREEN}fail2ban_helper${NC} commands."
TEXTS[main_help_f2b_test_rules_zh]="使用 ${YELLOW}fail2ban_helper test_rules${NC} 来运行集成测试。"
TEXTS[main_help_f2b_test_rules_en]="Use ${YELLOW}fail2ban_helper test_rules${NC} to run integration tests."
TEXTS[main_help_legacy_test_cmd_warn_zh]="旧的 'test' 命令已集成到 fail2ban_helper 中。"
TEXTS[main_help_legacy_test_cmd_warn_en]="The old 'test' command has been integrated into fail2ban_helper."
TEXTS[main_help_legacy_test_cmd_info_zh]="请使用: ${YELLOW}fail2ban_helper test_rules${NC} (需要先 source ~/.bashrc 使命令生效)"
TEXTS[main_help_legacy_test_cmd_info_en]="Please use: ${YELLOW}fail2ban_helper test_rules${NC} (requires sourcing ~/.bashrc first for the command to be available)"
TEXTS[main_help_legacy_test_cmd_alt_source_zh]="如果尚未 source，您可以临时 source 助手文件: ${YELLOW}source ~/.bash_caddy_f2b_helper && fail2ban_helper test_rules${NC}"
TEXTS[main_help_legacy_test_cmd_alt_source_en]="If you haven't sourced yet, you can temporarily source the helper file: ${YELLOW}source ~/.bash_caddy_f2b_helper && fail2ban_helper test_rules${NC}"
TEXTS[main_help_unknown_command_zh]="未知命令: %s. 显示帮助信息..."
TEXTS[main_help_unknown_command_en]="Unknown command: %s. Displaying help..."

# index.html content templates
TEXTS[index_html_content_tpl_zh]=$(cat << 'HTML_EOF_ZH'
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Caddy + Fail2Ban 演示</title>
    <style>
        body { font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; margin: 2em; background-color: #f4f4f9; color: #333; display: flex; justify-content: center; align-items: center; min-height: 90vh; }
        h1 { color: #2c3e50; text-align: center; }
        p { line-height: 1.6; }
        code { background-color: #e8e8e8; padding: 0.2em 0.4em; border-radius: 3px; font-family: 'Courier New', Courier, monospace; }
        .container { background-color: #fff; padding: 2em 3em; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); max-width: 600px; }
        .logo { font-size: 2.5em; text-align: center; margin-bottom: 0.5em; }
    </style>
</head>
<body>
    <div class="container">
        <div class="logo">🎉</div>
        <h1>Caddy 正在运行!</h1>
        <p>如果看到此页面，表示您的 Caddy Web 服务器已成功启动并通过 Docker 提供服务。</p>
        <p>Fail2Ban 正在后台默默守护您的服务器，监控来自宿主机路径 <code>%s</code> 的日志。</p>
        <p>祝您使用愉快！😊</p>
    </div>
</body>
</html>
HTML_EOF_ZH
)

TEXTS[index_html_content_tpl_en]=$(cat << 'HTML_EOF_EN'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Caddy + Fail2Ban Demo</title>
    <style>
        body { font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; margin: 2em; background-color: #f4f4f9; color: #333; display: flex; justify-content: center; align-items: center; min-height: 90vh; }
        h1 { color: #2c3e50; text-align: center; }
        p { line-height: 1.6; }
        code { background-color: #e8e8e8; padding: 0.2em 0.4em; border-radius: 3px; font-family: 'Courier New', Courier, monospace; }
        .container { background-color: #fff; padding: 2em 3em; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); max-width: 600px; }
        .logo { font-size: 2.5em; text-align: center; margin-bottom: 0.5em; }
    </style>
</head>
<body>
    <div class="container">
        <div class="logo">🎉</div>
        <h1>Caddy is Running!</h1>
        <p>If you see this page, your Caddy web server has successfully started and is serving via Docker.</p>
        <p>Fail2Ban is silently protecting your server in the background, monitoring logs from the host path <code>%s</code>.</p>
        <p>Enjoy using it! 😊</p>
    </div>
</body>
</html>
HTML_EOF_EN
)

# Helper Script Text
TEXTS[helper_script_title_zh]="Caddy 和 Fail2Ban 助手脚本"
TEXTS[helper_script_title_en]="Caddy and Fail2Ban Helper Script"
TEXTS[helper_script_generated_by_zh]="由安装脚本自动生成"
TEXTS[helper_script_generated_by_en]="Automatically generated by the installation script"

TEXTS[helper_usage_label_zh]="用法:"
TEXTS[helper_usage_label_en]="Usage:"
TEXTS[helper_command_label_zh]="命令"
TEXTS[helper_command_label_en]="command"
TEXTS[helper_param_label_zh]="参数"
TEXTS[helper_param_label_en]="parameter"
TEXTS[helper_help_option_zh]="help"
TEXTS[helper_help_option_en]="help"
TEXTS[helper_show_help_zh]="显示此帮助信息"
TEXTS[helper_show_help_en]="Display this help message"
TEXTS[helper_cmd_not_found_zh]="\${_HELPER_CONTAINER_ENGINE} 命令未找到。"
TEXTS[helper_cmd_not_found_en]="\${_HELPER_CONTAINER_ENGINE} command not found."
TEXTS[helper_container_not_running_zh]="容器 (%s) 未运行。请先用 '\${_H_YELLOW}\${_HELPER_COMPOSE_CMD} up -d\${_H_NC}' 启动 (在 \${_H_BLUE}\${_HELPER_INSTALL_DIR}\${_H_NC} 目录)。"
TEXTS[helper_container_not_running_en]="Container (%s) is not running. Please start it first with '\${_H_YELLOW}\${_HELPER_COMPOSE_CMD} up -d\${_H_NC}' (in \${_H_BLUE}\${_HELPER_INSTALL_DIR}\${_H_NC} directory)."
TEXTS[helper_invalid_command_zh]="无效命令: %s"
TEXTS[helper_invalid_command_en]="Invalid command: %s"

TEXTS[helper_caddy_title_zh]="Caddy 管理命令:"
TEXTS[helper_caddy_title_en]="Caddy Management Commands:"
TEXTS[helper_caddy_reload_desc_zh]="reload    - 校验 %s (会先自动格式化) 并平滑重载 Caddy 配置"
TEXTS[helper_caddy_reload_desc_en]="reload    - Validate %s (will be auto-formatted first) and gracefully reload Caddy configuration"
TEXTS[helper_caddy_logs_desc_zh]="logs      - 实时查看 Caddy 容器日志 (Ctrl+C 退出)"
TEXTS[helper_caddy_logs_desc_en]="logs      - View Caddy container logs in real-time (Ctrl+C to exit)"
TEXTS[helper_caddy_accesslog_desc_zh]="accesslog - 查看宿主机上 Caddy 访问日志的最后100行"
TEXTS[helper_caddy_accesslog_desc_en]="accesslog - View the last 100 lines of Caddy access logs on the host"
TEXTS[helper_caddy_shell_desc_zh]="shell     - 进入 Caddy 容器的 shell 环境"
TEXTS[helper_caddy_shell_desc_en]="shell     - Enter Caddy container's shell environment"
TEXTS[helper_caddy_status_desc_zh]="status    - 查看 Caddy 容器运行状态"
TEXTS[helper_caddy_status_desc_en]="status    - View Caddy container running status"
TEXTS[helper_caddy_vim_desc_zh]="vim       - 使用 vim 编辑 Caddyfile"
TEXTS[helper_caddy_vim_desc_en]="vim       - Edit Caddyfile with vim"
TEXTS[helper_caddy_formatting_caddyfile_zh]="格式化 Caddyfile: %s..."
TEXTS[helper_caddy_formatting_caddyfile_en]="Formatting Caddyfile: %s..."
TEXTS[helper_caddy_format_ok_zh]="\${_H_GREEN}✅ Caddyfile 格式化成功。\${_H_NC}"
TEXTS[helper_caddy_format_ok_en]="\${_H_GREEN}✅ Caddyfile formatted successfully.\${_H_NC}"
TEXTS[helper_caddy_reloading_zh]="尝试平滑重载 Caddy 配置..."
TEXTS[helper_caddy_reloading_en]="Attempting to gracefully reload Caddy configuration..."
TEXTS[helper_caddy_reload_ok_zh]="\${_H_GREEN}✅ Caddy 配置已成功重载。\${_H_NC}"
TEXTS[helper_caddy_reload_ok_en]="\${_H_GREEN}✅ Caddy configuration successfully reloaded.\${_H_NC}"
TEXTS[helper_caddy_reload_fail_zh]="Caddy 配置重载失败。请检查 Caddy 日志。"
TEXTS[helper_caddy_reload_fail_en]="Caddy configuration reload failed. Please check Caddy logs."
TEXTS[helper_caddy_format_fail_zh]="Caddyfile 格式化失败。请检查文件内容和 Caddy 日志。"
TEXTS[helper_caddy_format_fail_en]="Caddyfile formatting failed. Please check file content and Caddy logs."
TEXTS[helper_caddy_realtime_logs_zh]="实时查看 Caddy 容器日志 (按 Ctrl+C 退出)..."
TEXTS[helper_caddy_realtime_logs_en]="Viewing Caddy container logs in real-time (Press Ctrl+C to exit)..."
TEXTS[helper_caddy_access_log_header_zh]="\${_H_BLUE}=== Caddy 访问日志 (最后 100 行) ===\${_H_NC}"
TEXTS[helper_caddy_access_log_header_en]="\${_H_BLUE}=== Caddy Access Log (last 100 lines) ===\${_H_NC}"
TEXTS[helper_caddy_access_log_not_found_zh]="Caddy 访问日志 %s 未找到。"
TEXTS[helper_caddy_access_log_not_found_en]="Caddy access log %s not found."
TEXTS[helper_caddy_entering_shell_zh]="进入 Caddy 容器 (%s)... 输入 'exit' 退出。"
TEXTS[helper_caddy_entering_shell_en]="Entering Caddy container (%s)... Type 'exit' to quit."
TEXTS[helper_caddy_status_header_zh]="\${_H_BLUE}=== Caddy 容器状态 ===\${_H_NC}"
TEXTS[helper_caddy_status_header_en]="\${_H_BLUE}=== Caddy Container Status ===\${_H_NC}"
TEXTS[helper_caddy_opening_caddyfile_zh]="正在打开 Caddyfile: \${BLUE}%s\${NC} (Ctrl+Z 暂停, fg 恢复)"
TEXTS[helper_caddy_opening_caddyfile_en]="Opening Caddyfile: \${BLUE}%s\${NC} (Ctrl+Z to suspend, fg to resume)"

TEXTS[helper_f2b_title_zh]="Fail2Ban 管理命令:"
TEXTS[helper_f2b_title_en]="Fail2Ban Management Commands:"
TEXTS[helper_f2b_status_desc_zh]="status [监狱名]     - 查看所有或指定监狱的状态"
TEXTS[helper_f2b_status_desc_en]="status [jail_name]  - View status of all or a specific jail"
TEXTS[helper_f2b_unban_desc_zh]="unban <IP> [监狱名] - 从所有或指定监狱解封 IP"
TEXTS[helper_f2b_unban_desc_en]="unban <IP> [jail_name] - Unban IP from all or a specific jail"
TEXTS[helper_f2b_ips_desc_zh]="ips [监狱名]        - 显示所有或指定监狱当前封禁的 IP"
TEXTS[helper_f2b_ips_desc_en]="ips [jail_name]     - Display currently banned IPs for all or a specific jail"
TEXTS[helper_f2b_logs_desc_zh]="logs [-n LINES] [IP] - 查看 Fail2Ban 服务日志 (默认20行)"
TEXTS[helper_f2b_logs_desc_en]="logs [-n LINES] [IP] - View Fail2Ban service logs (default 20 lines)"
TEXTS[helper_f2b_reload_desc_zh]="reload              - 重新加载 Fail2Ban 配置"
TEXTS[helper_f2b_reload_desc_en]="reload              - Reload Fail2Ban configuration"
TEXTS[helper_f2b_ping_desc_zh]="ping                - 测试 Fail2Ban 服务是否在线"
TEXTS[helper_f2b_ping_desc_en]="ping                - Test if Fail2Ban service is online"
TEXTS[helper_f2b_configtest_desc_zh]="configtest          - 测试 Fail2Ban 配置文件语法"
TEXTS[helper_f2b_configtest_desc_en]="configtest          - Test Fail2Ban configuration file syntax"
TEXTS[helper_f2b_test_rules_desc_zh]="test_rules          - \${_H_YELLOW}(实验性)\${_H_NC} 运行集成的 Fail2Ban 规则有效性测试"
TEXTS[helper_f2b_test_rules_desc_en]="test_rules          - \${_H_YELLOW}(Experimental)\${_H_NC} Run integrated Fail2Ban rule validity tests"
TEXTS[helper_f2b_all_jails_status_zh]="\${_H_BLUE}=== 所有 Fail2Ban 监狱状态 ===\${_H_NC}"
TEXTS[helper_f2b_all_jails_status_en]="\${_H_BLUE}=== All Fail2Ban Jails Status ===\${_H_NC}"
TEXTS[helper_f2b_jail_status_zh]="\${_H_BLUE}=== 监狱 '%s' 状态 ===\${_H_NC}"
TEXTS[helper_f2b_jail_status_en]="\${_H_BLUE}=== Jail '%s' Status ===\${_H_NC}"
TEXTS[helper_f2b_unban_ip_required_zh]="需要指定要解封的 IP 地址。"
TEXTS[helper_f2b_unban_ip_required_en]="IP address to unban is required."
TEXTS[helper_f2b_unbanning_from_jail_zh]="从监狱 '\${_H_BLUE}%s\${_H_NC}' 解封 IP: \${_H_YELLOW}%s\${_H_NC}..."
TEXTS[helper_f2b_unbanning_from_jail_en]="Unbanning IP \${_H_YELLOW}%s\${_H_NC} from jail '\${_H_BLUE}%s\${_H_NC}'..."
TEXTS[helper_f2b_unban_success_zh]="\${_H_GREEN}✅ IP %s 已从监狱 %s 解封。\${_H_NC}"
TEXTS[helper_f2b_unban_success_en]="\${_H_GREEN}✅ IP %s unbanned from jail %s.\${_H_NC}"
TEXTS[helper_f2b_unban_fail_zh]="\${_H_RED}❌ 从监狱 %s 解封 IP %s 失败。\${_H_NC}"
TEXTS[helper_f2b_unban_fail_en]="\${_H_RED}❌ Failed to unban IP %s from jail %s.\${_H_NC}"
TEXTS[helper_f2b_unbanning_from_all_jails_zh]="从所有活动监狱解封 IP: \${_H_YELLOW}%s\${_H_NC}..."
TEXTS[helper_f2b_unbanning_from_all_jails_en]="Unbanning IP \${_H_YELLOW}%s\${_H_NC} from all active jails..."
TEXTS[helper_f2b_no_active_jails_zh]="未找到活动监狱。"
TEXTS[helper_f2b_no_active_jails_en]="No active jails found."
TEXTS[helper_f2b_unbanned_from_count_jails_zh]="\${_H_GREEN}✅ IP %s 已从 %s 个监狱中解封。\${_H_NC}"
TEXTS[helper_f2b_unbanned_from_count_jails_en]="\${_H_GREEN}✅ IP %s unbanned from %s jails.\${_H_NC}"
TEXTS[helper_f2b_ip_not_banned_zh]="\${_H_YELLOW}ℹ️  IP %s 在所有活动监狱中均未被封禁。\${_H_NC}"
TEXTS[helper_f2b_ip_not_banned_en]="\${_H_YELLOW}ℹ️  IP %s is not banned in any active jail.\${_H_NC}"
TEXTS[helper_f2b_unban_from_any_fail_zh]="\${_H_RED}❌ 未能从任何监狱解封 IP %s。\${_H_NC}"
TEXTS[helper_f2b_unban_from_any_fail_en]="\${_H_RED}❌ Failed to unban IP %s from any jail.\${_H_NC}"
TEXTS[helper_f2b_all_banned_ips_header_zh]="\${_H_BLUE}=== 所有监狱的封禁 IP 列表 ===\${_H_NC}"
TEXTS[helper_f2b_all_banned_ips_header_en]="\${_H_BLUE}=== Banned IP List for All Jails ===\${_H_NC}"
TEXTS[helper_f2b_no_active_jails_or_bans_zh]="\${_H_YELLOW}ℹ️  没有活动的监狱或没有IP被封禁。\${_H_NC}"
TEXTS[helper_f2b_no_active_jails_or_bans_en]="\${_H_YELLOW}ℹ️  No active jails or no IPs banned.\${_H_NC}"
TEXTS[helper_f2b_jail_label_zh]="\${_H_BLUE}监狱: %s\${_H_NC}"
TEXTS[helper_f2b_jail_label_en]="\${_H_BLUE}Jail: %s\${_H_NC}"
TEXTS[helper_f2b_no_banned_ips_in_jail_zh]="    此监狱没有封禁的IP。"
TEXTS[helper_f2b_no_banned_ips_in_jail_en]="    No banned IPs in this jail."
TEXTS[helper_f2b_all_jails_no_bans_zh]="\n\${_H_YELLOW}ℹ️  所有监狱均未发现封禁的IP。\${_H_NC}"
TEXTS[helper_f2b_all_jails_no_bans_en]="\n\${_H_YELLOW}ℹ️  No banned IPs found in any jail.\${_H_NC}"
TEXTS[helper_f2b_jail_banned_ips_header_zh]="\${_H_BLUE}=== 监狱 '%s' 的封禁 IP 列表 ===\${_H_NC}"
TEXTS[helper_f2b_jail_banned_ips_header_en]="\${_H_BLUE}=== Banned IP List for Jail '%s' ===\${_H_NC}"
TEXTS[helper_f2b_jail_no_bans_or_exists_zh]="监狱 '%s' 没有封禁的IP，或监狱不存在。"
TEXTS[helper_f2b_jail_no_bans_or_exists_en]="Jail '%s' has no banned IPs, or jail does not exist."
TEXTS[helper_f2b_logs_n_option_num_zh]="'-n' 选项需要一个数字参数。"
TEXTS[helper_f2b_logs_n_option_num_en]="'-n' option requires a numeric argument."
TEXTS[helper_f2b_logs_n_option_arg_zh]="'-n' 选项需要一个参数 (行数)。"
TEXTS[helper_f2b_logs_n_option_arg_en]="'-n' option requires an argument (number of lines)."
TEXTS[helper_f2b_recent_logs_header_zh]="\${_H_BLUE}=== 最近 %s 条 Fail2Ban 活动日志 ===\${_H_NC}"
TEXTS[helper_f2b_recent_logs_header_en]="\${_H_BLUE}=== Last %s Fail2Ban Activity Logs ===\${_H_NC}"
TEXTS[helper_f2b_no_log_entries_zh]="\${_H_YELLOW}ℹ️  未找到相关日志条目。\${_H_NC}"
TEXTS[helper_f2b_no_log_entries_en]="\${_H_YELLOW}ℹ️  No relevant log entries found.\${_H_NC}"
TEXTS[helper_f2b_ip_logs_header_zh]="\${_H_BLUE}=== IP '\${_H_YELLOW}%s\${_H_BLUE}' 的 Fail2Ban 活动日志 (最近 %s 条相关记录) ===\${_H_NC}"
TEXTS[helper_f2b_ip_logs_header_en]="\${_H_BLUE}=== Fail2Ban Activity Logs for IP '\${_H_YELLOW}%s\${_H_BLUE}' (last %s relevant records) ===\${_H_NC}"
TEXTS[helper_f2b_no_ip_logs_zh]="\${_H_YELLOW}ℹ️  未找到关于 IP %s 的封禁/解封日志。\${_H_NC}"
TEXTS[helper_f2b_no_ip_logs_en]="\${_H_YELLOW}ℹ️  No ban/unban logs found for IP %s.\${_H_NC}"
TEXTS[helper_f2b_reloading_config_zh]="重新加载 Fail2Ban 配置..."
TEXTS[helper_f2b_reloading_config_en]="Reloading Fail2Ban configuration..."
TEXTS[helper_f2b_reload_ok_zh]="\${_H_GREEN}✅ Fail2Ban 配置已成功重新加载。\${_H_NC}"
TEXTS[helper_f2b_reload_ok_en]="\${_H_GREEN}✅ Fail2Ban configuration successfully reloaded.\${_H_NC}"
TEXTS[helper_f2b_reload_fail_zh]="Fail2Ban 配置重新加载失败。"
TEXTS[helper_f2b_reload_fail_en]="Fail2Ban configuration reload failed."
TEXTS[helper_f2b_pinging_zh]="Pinging Fail2Ban server..."
TEXTS[helper_f2b_pinging_en]="Pinging Fail2Ban server..." # English is fine here
TEXTS[helper_f2b_ping_ok_zh]="\${_H_GREEN}✅ Fail2Ban 服务响应正常 (pong!) 👍\${_H_NC}"
TEXTS[helper_f2b_ping_ok_en]="\${_H_GREEN}✅ Fail2Ban service responded normally (pong!) 👍\${_H_NC}"
TEXTS[helper_f2b_ping_fail_zh]="Fail2Ban 服务未响应。"
TEXTS[helper_f2b_ping_fail_en]="Fail2Ban service did not respond."
TEXTS[helper_f2b_testing_config_zh]="测试 Fail2Ban 配置文件语法 (在容器内)..."
TEXTS[helper_f2b_testing_config_en]="Testing Fail2Ban configuration file syntax (inside container)..."
TEXTS[helper_f2b_config_ok_zh]="\${_H_GREEN}✅ Fail2Ban 配置文件语法正确。\${_H_NC}"
TEXTS[helper_f2b_config_ok_en]="\${_H_GREEN}✅ Fail2Ban configuration file syntax is correct.\${_H_NC}"
TEXTS[helper_f2b_config_fail_zh]="Fail2Ban 配置文件存在语法错误。请检查输出。"
TEXTS[helper_f2b_config_fail_en]="Fail2Ban configuration file has syntax errors. Please check the output."

TEXTS[helper_f2b_test_starting_zh]="🚧 \${_H_BLUE}开始运行 Fail2Ban 规则集成测试...\${_H_NC}"
TEXTS[helper_f2b_test_starting_en]="🚧 \${_H_BLUE}Starting Fail2Ban rule integration tests...\${_H_NC}"
TEXTS[helper_f2b_test_no_compose_cmd_zh]="未找到兼容的 Compose 命令。测试无法进行。"
TEXTS[helper_f2b_test_no_compose_cmd_en]="No compatible Compose command found. Tests cannot proceed."
TEXTS[helper_f2b_test_no_install_dir_zh]="安装目录 \${_H_BLUE}%s\${_H_NC} 未找到。请确保已正确安装。"
TEXTS[helper_f2b_test_no_install_dir_en]="Installation directory \${_H_BLUE}%s\${_H_NC} not found. Please ensure it's correctly installed."
TEXTS[helper_f2b_test_ensuring_services_zh]="🔄 确保 Caddy 和 Fail2Ban 服务正在运行..."
TEXTS[helper_f2b_test_ensuring_services_en]="🔄 Ensuring Caddy and Fail2Ban services are running..."
TEXTS[helper_f2b_test_starting_services_zh]="服务未完全启动或未启动，尝试启动服务: \${_H_YELLOW}%s up -d --build\${_H_NC}"
TEXTS[helper_f2b_test_starting_services_en]="Services not fully started or not started, attempting to start services: \${_H_YELLOW}%s up -d --build\${_H_NC}"
TEXTS[helper_f2b_test_waiting_services_zh]="⏳ 等待服务启动 (约 30秒)..."
TEXTS[helper_f2b_test_waiting_services_en]="⏳ Waiting for services to start (approx. 30 seconds)..."
TEXTS[helper_f2b_test_caddy_fail_zh]="Caddy 容器 (%s) 未能启动。日志: '\${_H_YELLOW}%s logs caddy\${_H_NC}'。"
TEXTS[helper_f2b_test_caddy_fail_en]="Caddy container (%s) failed to start. Logs: '\${_H_YELLOW}%s logs caddy\${_H_NC}'."
TEXTS[helper_f2b_test_f2b_fail_zh]="Fail2Ban 容器 (%s) 未能启动。日志: '\${_H_YELLOW}%s logs fail2ban\${_H_NC}'。"
TEXTS[helper_f2b_test_f2b_fail_en]="Fail2Ban container (%s) failed to start. Logs: '\${_H_YELLOW}%s logs fail2ban\${_H_NC}'."
TEXTS[helper_f2b_test_services_ok_zh]="\${_H_GREEN}✅ 服务正在运行。\${_H_NC} Caddy 日志 (宿主机): \${_H_BLUE}%s\${_H_NC}"
TEXTS[helper_f2b_test_services_ok_en]="\${_H_GREEN}✅ Services are running.\${_H_NC} Caddy log (host): \${_H_BLUE}%s\${_H_NC}"
TEXTS[helper_f2b_test_caddy_log_missing_zh]="Caddy 访问日志未创建，尝试生成初始日志..."
TEXTS[helper_f2b_test_caddy_log_missing_en]="Caddy access log not created, attempting to generate initial log..."
TEXTS[helper_f2b_test_caddy_log_still_missing_zh]="⚠️  Caddy 访问日志仍未找到，尝试手动创建: \${_H_BLUE}%s\${_H_NC}"
TEXTS[helper_f2b_test_caddy_log_still_missing_en]="⚠️  Caddy access log still not found, attempting to create manually: \${_H_BLUE}%s\${_H_NC}"
TEXTS[helper_f2b_test_caddy_log_create_fail_zh]="手动创建日志文件失败。"
TEXTS[helper_f2b_test_caddy_log_create_fail_en]="Manual creation of log file failed."
TEXTS[helper_f2b_test_cleaning_ip_zh]="🧹 \${_H_BLUE}全局清理测试 IP %s...\${_H_NC}"
TEXTS[helper_f2b_test_cleaning_ip_en]="🧹 \${_H_BLUE}Globally cleaning up test IP %s...\${_H_NC}"
TEXTS[helper_f2b_test_testing_jail_zh]="--- 🧪 测试监狱: \${_H_BLUE}%s\${_H_NC} ---"
TEXTS[helper_f2b_test_testing_jail_en]="--- 🧪 Testing jail: \${_H_BLUE}%s\${_H_NC} ---"
TEXTS[helper_f2b_test_jail_config_zh]="    配置: maxretry=%s"
TEXTS[helper_f2b_test_jail_config_en]="    Config: maxretry=%s"
TEXTS[helper_f2b_test_jail_simulating_zh]="    模拟: IP=%s, URI='%s', Status=%s%s, LogLevel=%s" # %s for optional UA part
TEXTS[helper_f2b_test_jail_simulating_en]="    Simulating: IP=%s, URI='%s', Status=%s%s, LogLevel=%s" # %s for optional UA part
TEXTS[helper_f2b_test_ua_contains_zh]=", UA contains '%s'"
TEXTS[helper_f2b_test_ua_contains_en]=", UA contains '%s'"
TEXTS[helper_f2b_test_wrote_logs_zh]="    已写入 %s 条日志。⏳ 等待 Fail2Ban 处理 (15-20 秒)..."
TEXTS[helper_f2b_test_wrote_logs_en]="    Wrote %s log entries. ⏳ Waiting for Fail2Ban to process (15-20 seconds)..."
TEXTS[helper_f2b_test_pass_zh]="    \${_H_GREEN}✅ 测试成功: IP %s 已被监狱 %s 封禁。\${_H_NC}"
TEXTS[helper_f2b_test_pass_en]="    \${_H_GREEN}✅ Test PASSED: IP %s was banned by jail %s.\${_H_NC}"
TEXTS[helper_f2b_test_fail_zh]="    \${_H_RED}❌ 测试失败: IP %s 未被监狱 %s 封禁。\${_H_NC}"
TEXTS[helper_f2b_test_fail_en]="    \${_H_RED}❌ Test FAILED: IP %s was NOT banned by jail %s.\${_H_NC}"
TEXTS[helper_f2b_test_related_logs_zh]="      🔎 \${_H_YELLOW}相关日志和状态:\${_H_NC}"
TEXTS[helper_f2b_test_related_logs_en]="      🔎 \${_H_YELLOW}Related logs and status:\${_H_NC}"
TEXTS[helper_f2b_test_caddy_log_tail_zh]="      Caddy 日志尾部 (\${_H_BLUE}%s\${_H_NC}):"
TEXTS[helper_f2b_test_caddy_log_tail_en]="      Caddy log tail (\${_H_BLUE}%s\${_H_NC}):"
TEXTS[helper_f2b_test_f2b_log_tail_zh]="      Fail2Ban 服务日志尾部:"
TEXTS[helper_f2b_test_f2b_log_tail_en]="      Fail2Ban service log tail:"
TEXTS[helper_f2b_test_jail_current_status_zh]="      监狱 \${_H_BLUE}%s\${_H_NC} 当前状态:"
TEXTS[helper_f2b_test_jail_current_status_en]="      Jail \${_H_BLUE}%s\${_H_NC} current status:"
TEXTS[helper_f2b_test_unbanning_ip_zh]="    解封 IP %s 从监狱 %s (清理)..."
TEXTS[helper_f2b_test_unbanning_ip_en]="    Unbanning IP %s from jail %s (cleanup)..."
TEXTS[helper_f2b_test_results_summary_zh]="--- 📊 \${_H_BLUE}测试结果汇总\${_H_NC} ---"
TEXTS[helper_f2b_test_results_summary_en]="--- 📊 \${_H_BLUE}Test Results Summary\${_H_NC} ---"
TEXTS[helper_f2b_test_all_passed_zh]="🎉🎉🎉 \${_H_GREEN}所有 Fail2Ban 规则测试均已通过！系统防护已就绪！\${_H_NC} 👍"
TEXTS[helper_f2b_test_all_passed_en]="🎉🎉🎉 \${_H_GREEN}All Fail2Ban rule tests passed! System protection is ready!\${_H_NC} 👍"
TEXTS[helper_f2b_test_some_failed_zh]="😥 \${_H_RED}部分 Fail2Ban 规则测试未通过。请仔细检查。\${_H_NC}"
TEXTS[helper_f2b_test_some_failed_en]="😥 \${_H_RED}Some Fail2Ban rule tests failed. Please review carefully.\${_H_NC}"
TEXTS[helper_f2b_test_possible_reasons_zh]="   \${_H_YELLOW}可能原因: filter 正则不匹配、jail.local 配置、日志未正确生成/传递等。\${_H_NC}"
TEXTS[helper_f2b_test_possible_reasons_en]="   \${_H_YELLOW}Possible reasons: filter regex mismatch, jail.local configuration, logs not generated/passed correctly, etc.\${_H_NC}"

TEXTS[helper_warn_engine_missing_completion_zh]="[WARN] \${_HELPER_CONTAINER_ENGINE:-容器引擎} 命令在生成助手脚本时未找到，命令补全功能可能无法正常工作。"
TEXTS[helper_warn_engine_missing_completion_en]="[WARN] \${_HELPER_CONTAINER_ENGINE:-Container engine} command not found when generating helper script, command completion may not work correctly."

# --- 国际化函数 ---
get_string() {
    local key="$1"
    shift # Remove key from arguments, rest are params for printf
    local params=("$@")
    local lang_suffix
    local found_string

    if [ "$SCRIPT_LANG" == "en" ]; then
        lang_suffix="_en"
    else
        lang_suffix="_zh"
    fi

    if [[ -v "TEXTS[${key}${lang_suffix}]" ]]; then
        found_string="${TEXTS[${key}${lang_suffix}]}"
    elif [[ -v "TEXTS[${key}_en]" ]]; then # Fallback to English if current lang not found
        found_string="${TEXTS[${key}_en]}"
    elif [[ -v "TEXTS[${key}_zh]" ]]; then # Fallback to Chinese if English (and current) not found
         found_string="${TEXTS[${key}_zh]}"
    elif [[ -v "TEXTS[${key}]" ]]; then # Fallback to a generic key if language specific is missing
        found_string="${TEXTS[${key}]}"
    else
        echo "!!!MISSING_STRING_${key}!!!" >&2
        return
    fi

    if [ ${#params[@]} -gt 0 ]; then
        # shellcheck disable=SC2059 # We control the format strings via TEXTS
        printf "$found_string" "${params[@]}"
    else
        echo -n "$found_string"
    fi
}

# --- 日志函数 (已国际化) ---
_log() {
    local color_prefix="$1"
    local level_key="$2" # e.g. "log_level_info"
    local message_key="$3"
    shift 3
    local message_params=("$@")

    local level_text
    level_text=$(get_string "$level_key") # Gets "信息" or "INFO"

    local message_content
    message_content=$(get_string "$message_key" "${message_params[@]}") # Gets the formatted message

    echo -e "${color_prefix}[${level_text}]${NC} ${message_content}"
}

info() { _log "${GREEN}" "log_level_info" "$@"; }
warn() { _log "${YELLOW}" "log_level_warn" "$@" >&2; }
error_exit() {
    _log "${RED}" "log_level_error" "$@" >&2
    exit 1
}

# --- 语言选择函数 ---
select_language() {
    echo -e "${YELLOW}${TEXTS[select_lang_prompt_bilingual]}${NC}"
    echo -e "${TEXTS[select_lang_option_zh_bilingual]}"
    echo -e "${TEXTS[select_lang_option_en_bilingual]}"
    local lang_choice
    read -r -p "$(echo -e "${YELLOW}${TEXTS[select_lang_input_prompt_bilingual]}${NC}")" lang_choice
    case "$lang_choice" in
        2) SCRIPT_LANG="en" ;;
        *) SCRIPT_LANG="zh" ;; # Default to Chinese if input is empty or invalid
    esac

    if [ "$SCRIPT_LANG" == "en" ]; then
        info "lang_set_to_english_en" # This call will use the new SCRIPT_LANG
    else
        info "lang_set_to_chinese_zh"
    fi
}


# --- 默认配置变量 (可在安装时修改) ---
DEFAULT_INSTALL_DIR="/opt/pod/caddy-fail2ban"
INSTALL_DIR=""
COMPOSE_FILE_NAME="compose.yaml"
COMPOSE_FILE_PATH=""
CADDY_CONFIG_DIR=""
CADDY_SITES_DIR=""
CADDY_LOG_DIR=""
FAIL2BAN_CONFIG_DIR=""
FAIL2BAN_JAIL_DIR=""
FAIL2BAN_FILTER_DIR=""
CADDY_ACCESS_LOG_HOST_PATH=""

# --- PUID/PGID 和容器引擎检测 ---
CURRENT_PUID=""
CURRENT_PGID=""
CONTAINER_ENGINE=""
COMPOSE_CMD=""

detect_user_ids_and_engine() {
    if [ "$(id -u)" -eq 0 ]; then
        warn "warn_running_as_root"
        warn "warn_root_puid_pgid"
        CURRENT_PUID="1000"
        CURRENT_PGID="1000"
    else
        info "info_running_as_user" "$(id -un)"
        CURRENT_PUID=$(id -u)
        CURRENT_PGID=$(id -g)
    fi
    info "info_container_puid_pgid" "${CURRENT_PUID}" "${CURRENT_PGID}"

    if command -v docker &> /dev/null; then
        CONTAINER_ENGINE="docker"
        if docker compose version &> /dev/null; then
            COMPOSE_CMD="docker compose"
        elif command -v docker-compose &> /dev/null; then
            COMPOSE_CMD="docker-compose"
            warn "warn_old_docker_compose"
        else
            error_exit "error_docker_compose_missing"
        fi
    elif command -v podman &> /dev/null; then
        CONTAINER_ENGINE="podman"
        if command -v podman-compose &> /dev/null; then
            COMPOSE_CMD="podman-compose"
        else
            warn "warn_podman_compose_missing"
        fi
    else
        error_exit "error_no_container_engine"
    fi
    info "info_using_container_engine" "${CONTAINER_ENGINE}"
    if [ -n "$COMPOSE_CMD" ]; then
        info "info_using_compose_command" "${COMPOSE_CMD}"
    else
        warn "warn_no_compose_command"
    fi
}

# --- 主要安装逻辑 ---
main_install() {
    info "info_begin_install"
    detect_user_ids_and_engine

    local input_dir
    local prompt_text
    prompt_text=$(get_string "prompt_install_dir" "$DEFAULT_INSTALL_DIR")
    read -e -p "$(echo -e "${YELLOW}${prompt_text}${NC}")" input_dir
    INSTALL_DIR=${input_dir:-"$DEFAULT_INSTALL_DIR"}
    info "info_install_dir_set" "${INSTALL_DIR}"

    COMPOSE_FILE_PATH="${INSTALL_DIR}/${COMPOSE_FILE_NAME}"
    CADDY_CONFIG_DIR="${INSTALL_DIR}/caddy"
    CADDY_SITES_DIR="${CADDY_CONFIG_DIR}/sites"
    CADDY_LOG_DIR="${CADDY_CONFIG_DIR}/logs"
    FAIL2BAN_CONFIG_DIR="${INSTALL_DIR}/fail2ban_conf"
    FAIL2BAN_JAIL_DIR="${FAIL2BAN_CONFIG_DIR}/fail2ban"
    FAIL2BAN_FILTER_DIR="${FAIL2BAN_JAIL_DIR}/filter.d"
    CADDY_ACCESS_LOG_HOST_PATH="${CADDY_LOG_DIR}/access.log"

    if [ "$(id -u)" -ne 0 ]; then
        local opt_dir
        opt_dir=$(dirname "$INSTALL_DIR")
        if [ ! -w "${opt_dir}" ] && [ ! -d "${INSTALL_DIR}" ]; then
             warn "warn_no_write_permission" "$(id -un)" "${opt_dir}" "${INSTALL_DIR}"
             warn "warn_permission_suggestion" "${opt_dir}"
             local confirm_continue
             local continue_prompt
             continue_prompt=$(get_string "prompt_continue_anyway")
             read -r -p "$(echo -e "${YELLOW}${continue_prompt}${NC}")" confirm_continue
            if [[ "$confirm_continue" != "y" && "$confirm_continue" != "Y" ]]; then
                info "info_install_cancelled"
                exit 0
            fi
        fi
    fi

    info "info_creating_files_in" "${INSTALL_DIR}"
    mkdir -p "${CADDY_SITES_DIR}"
    mkdir -p "${CADDY_LOG_DIR}"
    mkdir -p "${FAIL2BAN_FILTER_DIR}"
    info "info_dirs_created"

    info "info_creating_file" "${COMPOSE_FILE_PATH}"
    cat << EOF > "${COMPOSE_FILE_PATH}"
services:
  caddy:
    image: caddy:latest
    container_name: caddy
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    network_mode: host
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile
      - ./caddy/sites:/srv:ro
      - ./caddy/data:/data
      - ./caddy/config:/config
      - ./caddy/logs:/var/log/caddy

  fail2ban:
    image: lscr.io/linuxserver/fail2ban:latest
    container_name: fail2ban
    restart: unless-stopped
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    volumes:
      - ./fail2ban_conf:/config
      - ./caddy/logs:/remotelogs/caddy:ro
    environment:
      - PUID=${CURRENT_PUID}
      - PGID=${CURRENT_PGID}
      - TZ=Asia/Shanghai # $(get_string "compose_tz_comment")
    depends_on:
      caddy:
        condition: service_started
EOF
    info "info_file_created" "${COMPOSE_FILE_PATH}"

    info "info_creating_file" "${CADDY_CONFIG_DIR}/Caddyfile"
    cat << EOF > "${CADDY_CONFIG_DIR}/Caddyfile"
{
  admin localhost:2019
  log {
      output file /var/log/caddy/access.log {
          roll_size 10mb
          roll_keep 5
          roll_keep_for 720h
      }
      format json
      level INFO
  }
}

http://:80 {
    root * /srv
    file_server
    encode zstd gzip
}
EOF
    info "info_file_created" "${CADDY_CONFIG_DIR}/Caddyfile"
    info "info_caddyfile_http_port" "${CADDY_CONFIG_DIR}/Caddyfile"

    info "info_creating_file" "${CADDY_SITES_DIR}/index.html"
    local index_html_template
    index_html_template=$(get_string "index_html_content_tpl")
    # shellcheck disable=SC2059 # We control template
    printf "$index_html_template" "${CADDY_ACCESS_LOG_HOST_PATH}" > "${CADDY_SITES_DIR}/index.html"
    info "info_file_created" "${CADDY_SITES_DIR}/index.html"

    # 4. 创建 Fail2Ban jail.local
    info "info_creating_file" "$(get_string "Fail2Ban filter files")"
    mkdir -p "${FAIL2BAN_JAIL_DIR}" # 确保 fail2ban 子目录存在
    cat << EOF > "${FAIL2BAN_JAIL_DIR}/jail.local"
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 7d
bantime  = 1h
findtime = 10m
maxretry = 5

banaction = nftables-multiport

[caddy-auth-failure]
enabled = true
filter = caddy-auth-failure
logpath = /remotelogs/caddy/access.log
maxretry = 5
findtime = 10m
bantime = 30m
action = %(action_)s

[caddy-path-scan]
enabled = true
filter = caddy-path-scan
logpath = /remotelogs/caddy/access.log
maxretry = 3
findtime = 5m
bantime = 1h
action = %(action_)s

[caddy-malicious-request]
enabled = true
filter = caddy-malicious-request
logpath = /remotelogs/caddy/access.log
maxretry = 2
findtime = 10m
bantime = 1d
action = %(action_)s

[caddy-sql-injection]
enabled = true
filter = caddy-sql-injection
logpath = /remotelogs/caddy/access.log
maxretry = 1
findtime = 10m
bantime = 3d
action = %(action_)s

[caddy-server-error]
enabled = true
filter = caddy-server-error
logpath = /remotelogs/caddy/access.log
maxretry = 10
findtime = 5m
bantime = 1h
action = %(action_)s

[caddy-bad-crawler]
enabled = true
filter = caddy-bad-crawler
logpath = /remotelogs/caddy/access.log
maxretry = 1
findtime = 10m
bantime = 7d
action = %(action_)s

[caddy-brute-force]
enabled = true
filter = caddy-brute-force
logpath = /remotelogs/caddy/access.log
maxretry = 5
findtime = 5m
bantime = 2h
action = %(action_)s
EOF
    info "info_file_created" "${FAIL2BAN_JAIL_DIR}/jail.local"

    # 5. 创建 Fail2Ban filters
    info "info_creating_file" "$(get_string "Fail2Ban filter files")" # Generic message for multiple files
    cat << EOF > "${FAIL2BAN_FILTER_DIR}/caddy-auth-failure.conf"
[Definition]
failregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"<HOST>".*?"status":(401|403),.*$
ignoreregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"127\.0\.0\.1".*$
              ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"::1".*$

EOF

    cat << EOF > "${FAIL2BAN_FILTER_DIR}/caddy-bad-crawler.conf"
[Definition]
failregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"<HOST>".*?"headers":.*?"User-Agent":\[".*?(nmap|nikto|sqlmap|gobuster|dirb|wpscan|hydra|acunetix|qualys|nessus|nuclei|ZAP|burp).*?"\].*$
ignoreregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"127\.0\.0\.1".*$
              ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"::1".*$

EOF

    cat << EOF > "${FAIL2BAN_FILTER_DIR}/caddy-brute-force.conf"
[Definition]
failregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"<HOST>".*?"status":(401|403).*$
ignoreregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"127\.0\.0\.1".*$
              ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"::1".*$

EOF

    cat << EOF > "${FAIL2BAN_FILTER_DIR}/caddy-malicious-request.conf"
[Definition]
failregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"<HOST>".*?"uri":".*?(eval\(|exec\(|system\(|shell_exec\(|phpinfo|base64).*?".*?"status":(200|400|403|500).*$
ignoreregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"127\.0\.0\.1".*$
              ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"::1".*$

EOF

    cat << EOF > "${FAIL2BAN_FILTER_DIR}/caddy-path-scan.conf"
[Definition]
failregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"<HOST>".*?"uri":"(/\.env|/\.git/|/wp-login\.php|/wp-admin|/admin|/administrator|/phpmyadmin|/mysql|/sqlmanager|/cgi-bin|/xmlrpc\.php|/config\.php).*?".*?"status":404.*$
ignoreregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"127\.0\.0\.1".*$
              ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"::1".*$

EOF

    cat << EOF > "${FAIL2BAN_FILTER_DIR}/caddy-server-error.conf"
[Definition]
failregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"<HOST>".*?"status":5\d\d,.*$
ignoreregex = ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"127\.0\.0\.1".*$
              ^{"level":".*?","ts":.*?,"logger":".*?","msg":".*?","request":.*?"remote_ip":"::1".*$

EOF

    cat << EOF > "${FAIL2BAN_FILTER_DIR}/caddy-sql-injection.conf"
[Definition]
failregex = ^.*"remote_ip":"<HOST>".*"uri":".*%%20UNION%%20SELECT.*".*"status":(200|400|403|500).*$
            ^.*"remote_ip":"<HOST>".*"uri":".*%%20union%%20select.*".*"status":(200|400|403|500).*$
            ^.*"remote_ip":"<HOST>".*"uri":".*UNION.*SELECT.*".*"status":(200|400|403|500).*$
            ^.*"remote_ip":"<HOST>".*"uri":".*union.*select.*".*"status":(200|400|403|500).*$
            ^.*"remote_ip":"<HOST>".*"uri":".*%%27.*OR.*".*"status":(200|400|403|500).*$
            ^.*"remote_ip":"<HOST>".*"uri":".*%%22.*OR.*".*"status":(200|400|403|500).*$
ignoreregex = ^.*"remote_ip":"127\.0\.0\.1".*$
              ^.*"remote_ip":"::1".*$

EOF
    info "info_file_created" "$(get_string "Fail2Ban filter files")"
    info "info_fail2ban_filters_generated" "${FAIL2BAN_FILTER_DIR}"

    info "info_configuring_bash_env"
    local bashrc_file="${HOME}/.bashrc"
    local helper_script_file="${HOME}/.bash_caddy_f2b_helper"
    local source_line="if [ -f ${helper_script_file} ]; then . ${helper_script_file}; fi"

    if [ -f "$bashrc_file" ]; then
        if ! grep -qF -- "$source_line" "$bashrc_file"; then
            echo "" >> "$bashrc_file"
            echo "$source_line" >> "$bashrc_file"
            info "info_bashrc_source_added" "${helper_script_file}" "${bashrc_file}"
        else
            info "info_bashrc_source_exists" "${bashrc_file}" "${helper_script_file}"
        fi
    else
        warn "warn_bashrc_not_found" "${bashrc_file}"
        echo "$source_line"
    fi

    # --- 生成本地化的 Helper 脚本 ---
    cat << EOF > "${helper_script_file}"
# $(get_string "helper_script_title")
# $(get_string "helper_script_generated_by")

# --- Helper 脚本内部使用的变量 (由主安装脚本写入) ---
_HELPER_INSTALL_DIR="${INSTALL_DIR}"
_HELPER_COMPOSE_CMD="${COMPOSE_CMD}"
_HELPER_CONTAINER_ENGINE="${CONTAINER_ENGINE}"
_HELPER_CADDY_ACCESS_LOG_HOST_PATH="${CADDY_ACCESS_LOG_HOST_PATH}"
_HELPER_CADDY_CONFIG_DIR_HOST="${CADDY_CONFIG_DIR}"

# --- Helper 脚本内部使用的颜色定义 ---
if [ -t 1 ]; then
    _H_RED='\033[0;31m'; _H_GREEN='\033[0;32m'; _H_YELLOW='\033[1;33m'; _H_BLUE='\033[0;34m'; _H_NC='\033[0m'
else
    _H_RED=''; _H_GREEN=''; _H_YELLOW=''; _H_BLUE=''; _H_NC=''
fi

# --- Helper 脚本内部使用的日志函数 (文本已预先翻译) ---
_h_log() { local color_prefix="\$1"; local level_text="\$2"; local message="\$3"; echo -e "\${color_prefix}[\${level_text}]\${_H_NC} \${message}"; }
_h_info() { _h_log "\${_H_GREEN}" "$(get_string "log_level_info")" "\$1"; }
_h_warn() { _h_log "\${_H_YELLOW}" "$(get_string "log_level_warn")" "\$1" >&2; }
_h_error_exit() { _h_log "\${_H_RED}" "$(get_string "log_level_error")" "\$1" >&2; return 1; }

# --- Caddy 管理函数 ---
caddy_helper() {
    local usage="
\${_H_BLUE}$(get_string "helper_usage_label")\${_H_NC} caddy_helper [$(get_string "helper_command_label")]
\${_H_BLUE}$(get_string "helper_caddy_title")\${_H_NC}
    $(get_string "helper_caddy_reload_desc" "\${_H_BLUE}\${_HELPER_CADDY_CONFIG_DIR_HOST}/Caddyfile\${_H_NC}")
    $(get_string "helper_caddy_logs_desc")
    $(get_string "helper_caddy_accesslog_desc")
    $(get_string "helper_caddy_shell_desc")
    $(get_string "helper_caddy_status_desc")
    $(get_string "helper_caddy_vim_desc")
    $(get_string "helper_help_option")      - $(get_string "helper_show_help")
"
    local caddy_container_name="caddy"
    local caddyfile_container_path="/etc/caddy/Caddyfile"
    if ! command -v "\${_HELPER_CONTAINER_ENGINE}" &> /dev/null; then
        _h_error_exit "$(get_string "helper_cmd_not_found")"
        return 1
    fi
    if [[ "\$1" == "$(get_string "helper_help_option")" || -z "\$1" ]]; then echo -e "\$usage"; return 0; fi

    if ! \${_HELPER_CONTAINER_ENGINE} ps -q --filter "name=^/\${caddy_container_name}$" | grep -q . && [[ "\$1" != "status" ]]; then
        _h_error_exit "$(printf "$(get_string "helper_container_not_running")" "\${caddy_container_name}")"
        return 1
    fi
    case "\$1" in
        reload)
            _h_info "$(printf "$(get_string "helper_caddy_formatting_caddyfile")" "\${_H_BLUE}\${_HELPER_CADDY_CONFIG_DIR_HOST}/Caddyfile\${_H_NC}")"
            if \${_HELPER_CONTAINER_ENGINE} exec "\${caddy_container_name}" caddy fmt --overwrite "\${caddyfile_container_path}"; then
                _h_info "$(get_string "helper_caddy_format_ok")"
                _h_info "$(get_string "helper_caddy_reloading")"
                if \${_HELPER_CONTAINER_ENGINE} exec "\${caddy_container_name}" caddy reload --config "\${caddyfile_container_path}"; then
                    _h_info "$(get_string "helper_caddy_reload_ok")"
                else _h_error_exit "$(get_string "helper_caddy_reload_fail")"; fi
            else _h_error_exit "$(get_string "helper_caddy_format_fail")"; fi
            ;;
        logs)
            _h_info "$(get_string "helper_caddy_realtime_logs")"
            \${_HELPER_CONTAINER_ENGINE} logs -f "\${caddy_container_name}"
            ;;
        accesslog)
            if [ -f "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}" ]; then
                _h_info "$(get_string "helper_caddy_access_log_header")"
                tail -n 100 "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}"
            else _h_warn "$(printf "$(get_string "helper_caddy_access_log_not_found")" "\${_H_BLUE}\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}\${_H_NC}")"; fi
            ;;
        shell)
            _h_info "$(printf "$(get_string "helper_caddy_entering_shell")" "\${caddy_container_name}")"
            \${_HELPER_CONTAINER_ENGINE} exec -it "\${caddy_container_name}" sh
            ;;
        status)
            _h_info "$(get_string "helper_caddy_status_header")"
            \${_HELPER_CONTAINER_ENGINE} ps -a --filter "name=^/\${caddy_container_name}$"
            ;;
        vim)
            _h_info "$(printf "$(get_string "helper_caddy_opening_caddyfile")" "\${_H_BLUE}\${_HELPER_CADDY_CONFIG_DIR_HOST}/Caddyfile\${_H_NC}")"
            vim "\${_HELPER_CADDY_CONFIG_DIR_HOST}/Caddyfile"
            ;;
        *)
            _h_error_exit "$(printf "$(get_string "helper_invalid_command")" "\$1")"
            echo -e "\$usage"
            return 1
            ;;
    esac
}

# --- Fail2Ban 管理与测试函数 ---
fail2ban_helper() {
    local usage="
\${_H_BLUE}$(get_string "helper_usage_label")\${_H_NC} fail2ban_helper [$(get_string "helper_command_label")] [$(get_string "helper_param_label")]

\${_H_BLUE}$(get_string "helper_f2b_title")\${_H_NC}
    $(get_string "helper_f2b_status_desc")
    $(get_string "helper_f2b_unban_desc")
    $(get_string "helper_f2b_ips_desc")
    $(get_string "helper_f2b_logs_desc")
    $(get_string "helper_f2b_reload_desc")
    $(get_string "helper_f2b_ping_desc")
    $(get_string "helper_f2b_configtest_desc")
    $(get_string "helper_f2b_test_rules_desc")
    $(get_string "helper_help_option")                - $(get_string "helper_show_help")
"
    local f2b_container_name="fail2ban"

    if ! command -v "\${_HELPER_CONTAINER_ENGINE}" &> /dev/null; then
        _h_error_exit "$(get_string "helper_cmd_not_found")"
        return 1
    fi

    if [[ "\$1" == "$(get_string "helper_help_option")" || -z "\$1" ]]; then echo -e "\$usage"; return 0; fi

    if [[ "\$1" == "test_rules" ]]; then
        _f2b_run_integration_tests
        return \$?
    fi

    if ! \${_HELPER_CONTAINER_ENGINE} ps -q --filter "name=^/\${f2b_container_name}$" | grep -q .; then
        _h_error_exit "$(printf "$(get_string "helper_container_not_running")" "\${f2b_container_name}")"
        return 1
    fi

    case "\$1" in
        status|details)
            if [ -z "\$2" ]; then
                _h_info "$(get_string "helper_f2b_all_jails_status")"
                \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client status
            else
                _h_info "$(printf "$(get_string "helper_f2b_jail_status")" "\$2")"
                \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client status "\$2"
            fi
            ;;
        unban)
            if [ -z "\$2" ]; then _h_error_exit "$(get_string "helper_f2b_unban_ip_required")"; echo -e "\$usage"; return 1; fi
            local ip_to_unban="\$2"; local jail_to_unban_from="\$3"
            if [ -n "\$jail_to_unban_from" ]; then
                _h_info "$(printf "$(get_string "helper_f2b_unbanning_from_jail")" "\${jail_to_unban_from}" "\${ip_to_unban}")"
                if \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client set "\${jail_to_unban_from}" unbanip "\${ip_to_unban}"; then
                    _h_info "$(printf "$(get_string "helper_f2b_unban_success")" "\${ip_to_unban}" "\${jail_to_unban_from}")"
                else _h_warn "$(printf "$(get_string "helper_f2b_unban_fail")" "\${jail_to_unban_from}" "\${ip_to_unban}")"; fi
            else
                _h_info "$(printf "$(get_string "helper_f2b_unbanning_from_all_jails")" "\${ip_to_unban}")"
                local jails; jails=\$(${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client status 2>/dev/null | grep "Jail list" | sed -E "s/^[^:]+:[ \t]+//g" | sed "s/,//g")
                if [ -z "\$jails" ]; then _h_warn "$(get_string "helper_f2b_no_active_jails")"; return 1; fi
                local unbanned_count=0; local actually_banned_in_jail=false
                for jail in \$jails; do
                    if \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client status "\${jail}" | grep -q "Banned IP list:.*\${ip_to_unban}"; then
                        actually_banned_in_jail=true
                        if \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client set "\${jail}" unbanip "\${ip_to_unban}"; then unbanned_count=\$((unbanned_count + 1)); fi
                    fi
                done
                if [ "\$unbanned_count" -gt 0 ]; then _h_info "$(printf "$(get_string "helper_f2b_unbanned_from_count_jails")" "\${ip_to_unban}" "\${unbanned_count}")";
                elif [ "\$actually_banned_in_jail" = false ]; then _h_info "$(printf "$(get_string "helper_f2b_ip_not_banned")" "\${ip_to_unban}")";
                else _h_warn "$(printf "$(get_string "helper_f2b_unban_from_any_fail")" "\${ip_to_unban}")"; fi
            fi
            ;;
        ips)
            local target_jail="\$2"
            if [ -z "\$target_jail" ]; then
                _h_info "$(get_string "helper_f2b_all_banned_ips_header")"
                \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" bash -c '
                    jails=\$(fail2ban-client status | grep "Jail list" | sed -E "s/^[^:]+:[ \t]+//g" | sed "s/,//g")
                    if [ -z "\$jails" ]; then echo -e "'"\$(get_string "helper_f2b_no_active_jails_or_bans")"'"; exit 0; fi
                    found_any=false
                    for jail in \$jails; do
                        echo -e "\n  $(printf "$(get_string "helper_f2b_jail_label")" "\${jail}")"
                        banned_ips=\$(fail2ban-client status "\$jail" | grep "Banned IP list:" | sed "s/.*Banned IP list:[ \t]*//")
                        if [ -n "\$banned_ips" ]; then echo -e "    \${_H_YELLOW}\$banned_ips\${_H_NC}"; found_any=true;
                        else echo -e "'"\$(get_string "helper_f2b_no_banned_ips_in_jail")"'"; fi
                    done
                    if [ "\$found_any" = false ]; then echo -e "'"\$(get_string "helper_f2b_all_jails_no_bans")"'"; fi
                '
            else
                _h_info "$(printf "$(get_string "helper_f2b_jail_banned_ips_header")" "\${target_jail}")"
                local banned_ips; banned_ips=\$(${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client status "\${target_jail}" | grep "Banned IP list:" | sed "s/.*Banned IP list:[ \t]*//")
                if [ -n "\$banned_ips" ]; then echo -e "  \${_H_YELLOW}\$banned_ips\${_H_NC}";
                else _h_warn "$(printf "$(get_string "helper_f2b_jail_no_bans_or_exists")" "\${target_jail}")"; fi
            fi
            ;;
        logs)
            local num_lines=20; local filter_ip=""
            if [[ "\$2" == "-n" && -n "\$3" ]]; then
                if [[ "\$3" =~ ^[0-9]+$ ]]; then num_lines="\$3"; shift 2;
                else _h_error_exit "$(get_string "helper_f2b_logs_n_option_num")"; return 1; fi
            elif [[ "\$2" == "-n" ]]; then _h_error_exit "$(get_string "helper_f2b_logs_n_option_arg")"; return 1; fi
            if [ -n "\$2" ]; then filter_ip="\$2"; fi
            local log_file_container="/config/log/fail2ban/fail2ban.log"
            if [ -z "\$filter_ip" ]; then
                _h_info "$(printf "$(get_string "helper_f2b_recent_logs_header")" "\${num_lines}")"
                \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" tail -n "\${num_lines}" "\${log_file_container}" | grep -E --color=never "Ban |Unban |Start|Stop" || _h_info "$(get_string "helper_f2b_no_log_entries")"
            else
                _h_info "$(printf "$(get_string "helper_f2b_ip_logs_header")" "\${filter_ip}" "\${num_lines}")"
                \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" grep -E --color=never "Ban .*\${filter_ip}|Unban .*\${filter_ip}" "\${log_file_container}" | tail -n "\${num_lines}" || _h_info "$(printf "$(get_string "helper_f2b_no_ip_logs")" "\${filter_ip}")"
            fi
            ;;
        reload)
            _h_info "$(get_string "helper_f2b_reloading_config")"
            if \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client reload; then
                 _h_info "$(get_string "helper_f2b_reload_ok")"
            else _h_error_exit "$(get_string "helper_f2b_reload_fail")"; fi
            ;;
        ping)
            _h_info "$(get_string "helper_f2b_pinging")"
            if \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client ping | grep -q "Server replied: pong"; then
                _h_info "$(get_string "helper_f2b_ping_ok")"
            else _h_error_exit "$(get_string "helper_f2b_ping_fail")"; fi
            ;;
        configtest)
            _h_info "$(get_string "helper_f2b_testing_config")"
            if \${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-server --test -vvv; then
                 _h_info "$(get_string "helper_f2b_config_ok")"
            else _h_error_exit "$(get_string "helper_f2b_config_fail")"; fi
            ;;
        *) _h_error_exit "$(printf "$(get_string "helper_invalid_command")" "\$1")"; echo -e "\$usage"; return 1;;
    esac
}

_f2b_generate_log_entry() {
    local host="\$1"; local uri="\$2"; local status_code="\$3"
    local user_agent="\${4:-IntegrationTestAgent/1.0}"; local log_level="\${5:-info}"
    local current_ts; current_ts=\$(LC_ALL=C date +%s.%N)
    cat << LOG_EOF
{"level":"\${log_level}","ts":\${current_ts},"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"\${host}","remote_port":"\$(shuf -i 10000-60000 -n 1)","proto":"HTTP/1.1","method":"GET","host":"testhost.local","uri":"\${uri}","headers":{"User-Agent":["\${user_agent}"],"Accept":["*/*"],"X-Forwarded-For":[]},"tls":null},"user_id":"","duration":0.000\$(shuf -i 100-999 -n 1),"size":\$(shuf -i 50-500 -n 1),"status":\${status_code},"resp_headers":{"Server":["Caddy"],"Content-Type":["text/plain; charset=utf-8"]}}
LOG_EOF
}

_f2b_run_integration_tests() {
    _h_info "$(get_string "helper_f2b_test_starting")"
    if [ -z "\${_HELPER_COMPOSE_CMD}" ]; then
        _h_error_exit "$(get_string "helper_f2b_test_no_compose_cmd")"
        return 1
    fi
    if [ ! -d "\${_HELPER_INSTALL_DIR}" ]; then
        _h_error_exit "$(printf "$(get_string "helper_f2b_test_no_install_dir")" "\${_HELPER_INSTALL_DIR}")"
        return 1
    fi

    local TEST_IP="1.2.3.4"
    local CADDY_CONTAINER="caddy"
    local F2B_CONTAINER="fail2ban"

    cd "\${_HELPER_INSTALL_DIR}" || { _h_error_exit "$(printf "$(get_string "helper_f2b_test_no_install_dir")" "\${_HELPER_INSTALL_DIR}")"; return 1; }

    _h_info "$(get_string "helper_f2b_test_ensuring_services")"
    if ! \${_HELPER_CONTAINER_ENGINE} ps -q --filter "name=^/\${CADDY_CONTAINER}$" | grep -q . || \
       ! \${_HELPER_CONTAINER_ENGINE} ps -q --filter "name=^/\${F2B_CONTAINER}$" | grep -q .; then
        _h_info "$(printf "$(get_string "helper_f2b_test_starting_services")" "\${_HELPER_COMPOSE_CMD}")"
        \${_HELPER_COMPOSE_CMD} up -d --build
        _h_info "$(get_string "helper_f2b_test_waiting_services")"; sleep 30
    fi

    if ! \${_HELPER_CONTAINER_ENGINE} ps -q --filter "name=^/\${CADDY_CONTAINER}$" | grep -q .; then
        _h_error_exit "$(printf "$(get_string "helper_f2b_test_caddy_fail")" "\${CADDY_CONTAINER}" "\${_HELPER_COMPOSE_CMD}")"
        return 1
    fi
    if ! \${_HELPER_CONTAINER_ENGINE} ps -q --filter "name=^/\${F2B_CONTAINER}$" | grep -q .; then
        _h_error_exit "$(printf "$(get_string "helper_f2b_test_f2b_fail")" "\${F2B_CONTAINER}" "\${_HELPER_COMPOSE_CMD}")"
        return 1
    fi
    _h_info "$(printf "$(get_string "helper_f2b_test_services_ok")" "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}")"

    if [ ! -f "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}" ]; then
        _h_info "$(get_string "helper_f2b_test_caddy_log_missing")";
        curl -s -o /dev/null --max-time 3 "http://localhost/" || true; sleep 3
        if [ ! -f "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}" ]; then
            _h_warn "$(printf "$(get_string "helper_f2b_test_caddy_log_still_missing")" "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}")"
            touch "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}" || _h_warn "$(get_string "helper_f2b_test_caddy_log_create_fail")"
        fi
    fi

    declare -A jail_test_params
    jail_test_params["caddy-auth-failure"]="5 /restricted-area 401"
    jail_test_params["caddy-path-scan"]="3 /.git/HEAD 404"
    jail_test_params["caddy-malicious-request"]="2 /?q=eval(phpinfo()) 200"
    jail_test_params["caddy-sql-injection"]="1 /search?id=1%20UNION%20SELECT%20@@version 200"
    jail_test_params["caddy-server-error"]="10 /generate-503-error 503 ignored_ua error"
    jail_test_params["caddy-bad-crawler"]="1 /assets/style.css 200 nmap"
    jail_test_params["caddy-brute-force"]="5 /dologin 401"

    local all_tests_passed=true; local overall_summary=()

    _h_info "$(printf "$(get_string "helper_f2b_test_cleaning_ip")" "\${TEST_IP}")"
    local jails_to_clean; jails_to_clean=\$(${_HELPER_CONTAINER_ENGINE} exec "\${F2B_CONTAINER}" fail2ban-client status 2>/dev/null | grep "Jail list" | sed -E "s/^[^:]+:[ \t]+//g" | sed "s/,//g" 2>/dev/null || echo "")
    if [ -n "\$jails_to_clean" ]; then
        for j_clean in \$jails_to_clean; do
            \${_HELPER_CONTAINER_ENGINE} exec "\${F2B_CONTAINER}" fail2ban-client set "\$j_clean" unbanip "\${TEST_IP}" > /dev/null 2>&1
        done
    fi; sleep 2

    for jail_name in "\${!jail_test_params[@]}"; do
        local params_array=(\${jail_test_params[\$jail_name]})
        local max_retry="\${params_array[0]}"; local test_uri="\${params_array[1]}"
        local status_code="\${params_array[2]}"; local user_agent_part="\${params_array[3]:-}"
        local log_level_part="\${params_array[4]:-info}"
        local ua_string=""
        if [ -n "\$user_agent_part" ] && [ "\$user_agent_part" != "ignored_ua" ]; then
            ua_string="$(printf "$(get_string "helper_f2b_test_ua_contains")" "\${user_agent_part}")"
        fi

        _h_info ""; _h_info "$(printf "$(get_string "helper_f2b_test_testing_jail")" "\${jail_name}")"
        _h_info "$(printf "$(get_string "helper_f2b_test_jail_config")" "\${max_retry}")"
        _h_info "$(printf "$(get_string "helper_f2b_test_jail_simulating")" "\${TEST_IP}" "\${test_uri}" "\${status_code}" "\${ua_string}" "\${log_level_part}")"


        local actual_user_agent="IntegrationTestAgent/1.0"
        if [ "\$jail_name" == "caddy-bad-crawler" ] && [ -n "\$user_agent_part" ] && [ "\$user_agent_part" != "ignored_ua" ]; then
            actual_user_agent="Prefix \${user_agent_part} Suffix"
        fi

        for i in \$(seq 1 "\${max_retry}"); do
            log_line=\$(_f2b_generate_log_entry "\${TEST_IP}" "\${test_uri}" "\${status_code}" "\${actual_user_agent}" "\${log_level_part}")
            echo "\${log_line}" >> "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}"
            if [ "\$i" -lt "\${max_retry}" ]; then sleep 0.1; fi
        done

        _h_info "$(printf "$(get_string "helper_f2b_test_wrote_logs")" "\${max_retry}")"; sleep 20

        if \${_HELPER_CONTAINER_ENGINE} exec "\${F2B_CONTAINER}" fail2ban-client status "\${jail_name}" | grep -Fq -- "\${TEST_IP}"; then
            _h_info "$(printf "$(get_string "helper_f2b_test_pass")" "\${TEST_IP}" "\${jail_name}")"
            overall_summary+=("✅ \${jail_name}: PASSED")
        else
            _h_warn "$(printf "$(get_string "helper_f2b_test_fail")" "\${TEST_IP}" "\${jail_name}")"
            all_tests_passed=false; overall_summary+=("❌ \${jail_name}: FAILED")
            _h_warn "$(get_string "helper_f2b_test_related_logs")"
            _h_warn "$(printf "$(get_string "helper_f2b_test_caddy_log_tail")" "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}")"; tail -n "\$((\max_retry + 5))" "\${_HELPER_CADDY_ACCESS_LOG_HOST_PATH}" | sed 's/^/        /'
            _h_warn "$(get_string "helper_f2b_test_f2b_log_tail")"; \${_HELPER_CONTAINER_ENGINE} exec "\${F2B_CONTAINER}" tail -n 20 /config/log/fail2ban/fail2ban.log | sed 's/^/        /'
            _h_warn "$(printf "$(get_string "helper_f2b_test_jail_current_status")" "\${jail_name}")"; \${_HELPER_CONTAINER_ENGINE} exec "\${F2B_CONTAINER}" fail2ban-client status "\${jail_name}" | sed 's/^/        /'
        fi

        _h_info "$(printf "$(get_string "helper_f2b_test_unbanning_ip")" "\${TEST_IP}" "\${jail_name}")"
        \${_HELPER_CONTAINER_ENGINE} exec "\${F2B_CONTAINER}" fail2ban-client set "\${jail_name}" unbanip "\${TEST_IP}" > /dev/null 2>&1; sleep 2
    done

    _h_info ""; _h_info "$(get_string "helper_f2b_test_results_summary")"
    for summary_item in "\${overall_summary[@]}"; do
        if [[ "\$summary_item" == ✅* ]]; then echo -e "  \${_H_GREEN}\${summary_item}\${_H_NC}";
        else echo -e "  \${_H_RED}\${summary_item}\${_H_NC}"; fi
    done; echo ""

    if \$all_tests_passed; then
        _h_info "$(get_string "helper_f2b_test_all_passed")"
    else
        _h_warn "$(get_string "helper_f2b_test_some_failed")"
        _h_warn "$(get_string "helper_f2b_test_possible_reasons")"
    fi
    return 0
}

# 设置命令补全
_helper_script_complete() {
    local cur prev command_to_complete opts jails
    COMPREPLY=()
    if [ -z "\${COMP_WORDS-}" ] || [ -z "\${COMP_CWORD-}" ]; then return 0; fi

    cur="\${COMP_WORDS[COMP_CWORD]}"
    prev="\${COMP_WORDS[COMP_CWORD-1]}"
    command_to_complete="\${COMP_WORDS[0]}"

    local f2b_container_name="fail2ban"

    if [[ "\${command_to_complete}" == "fail2ban_helper" ]]; then
        opts="status unban ips logs reload ping configtest test_rules $(get_string "helper_help_option")"
        case "\${prev}" in
            fail2ban_helper) COMPREPLY=( \$(compgen -W "\${opts}" -- \${cur}) ); return 0;;
            status|ips|details)
                if command -v "\${_HELPER_CONTAINER_ENGINE}" &> /dev/null && \${_HELPER_CONTAINER_ENGINE} ps -q --filter "name=^/\${f2b_container_name}$" | grep -q .; then
                    jails=\$(${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client status 2>/dev/null | grep "Jail list" | sed -E "s/^[^:]+:[ \t]+//g" | sed "s/,//g" 2>/dev/null)
                    COMPREPLY=( \$(compgen -W "\${jails}" -- \${cur}) )
                fi; return 0;;
            unban)
                if [ "\${COMP_CWORD}" -eq 3 ]; then
                    if command -v "\${_HELPER_CONTAINER_ENGINE}" &> /dev/null && \${_HELPER_CONTAINER_ENGINE} ps -q --filter "name=^/\${f2b_container_name}$" | grep -q .; then
                        jails=\$(${_HELPER_CONTAINER_ENGINE} exec "\${f2b_container_name}" fail2ban-client status 2>/dev/null | grep "Jail list" | sed -E "s/^[^:]+:[ \t]+//g" | sed "s/,//g" 2>/dev/null)
                        COMPREPLY=( \$(compgen -W "\${jails}" -- \${cur}) )
                    fi
                fi; return 0;;
            logs) if [[ "\$cur" == -* ]]; then COMPREPLY=( \$(compgen -W "-n" -- "\$cur") ); fi; return 0;;
        esac
    elif [[ "\${command_to_complete}" == "caddy_helper" ]]; then
        opts="reload logs accesslog shell status vim $(get_string "helper_help_option")"
         if [[ "\${prev}" == "caddy_helper" ]]; then COMPREPLY=( \$(compgen -W "\${opts}" -- \${cur}) ); fi
        return 0
    fi
}

if command -v "\${_HELPER_CONTAINER_ENGINE}" &> /dev/null || [ -n "\${_HELPER_CONTAINER_ENGINE}" ]; then
    complete -F _helper_script_complete fail2ban_helper
    complete -F _helper_script_complete caddy_helper
else
    echo "$(get_string "helper_warn_engine_missing_completion")" >&2
fi
EOF
    chmod +x "${helper_script_file}"
    info "info_helper_script_created" "${helper_script_file}"
    info "info_run_source_bashrc"

    echo ""
    info "info_install_complete"
    info "info_service_directory" "${INSTALL_DIR}"
    if [ -n "$COMPOSE_CMD" ]; then
        info "info_start_service" "${INSTALL_DIR}" "${COMPOSE_CMD}"
        info "info_stop_service" "${INSTALL_DIR}" "${COMPOSE_CMD}"
        info "info_view_caddy_logs"
        info "info_view_f2b_logs"
        info "info_test_f2b_rules"
    else
        warn "warn_manual_container_management"
    fi

    echo ""
    echo -e "${GREEN}===================================================================${NC}"
    info "info_helper_commands_added"
    echo ""
    echo -e "$(get_string "info_caddy_helper_help")"
    echo -e "$(get_string "info_f2b_helper_help")"
    echo ""
    echo -e "${GREEN}===================================================================${NC}"
}

# --- 卸载功能 ---
uninstall_services() {
    info "info_begin_uninstall"
    if [ -z "$INSTALL_DIR" ]; then
        local helper_script_file_for_uninstall="${HOME}/.bash_caddy_f2b_helper"
        if [ -f "$helper_script_file_for_uninstall" ]; then
            # shellcheck source=/dev/null
            INSTALL_DIR=$(grep '^_HELPER_INSTALL_DIR=' "$helper_script_file_for_uninstall" | cut -d'"' -f2)
            COMPOSE_CMD=$(grep '^_HELPER_COMPOSE_CMD=' "$helper_script_file_for_uninstall" | cut -d'"' -f2)
            if [ -z "$INSTALL_DIR" ]; then
                 warn "warn_cannot_determine_install_dir" "$DEFAULT_INSTALL_DIR"
                 INSTALL_DIR="$DEFAULT_INSTALL_DIR"
            else
                info "info_install_dir_from_helper" "${INSTALL_DIR}"
            fi
        else
            warn "warn_helper_script_not_found_uninstall" "$DEFAULT_INSTALL_DIR"
            INSTALL_DIR="$DEFAULT_INSTALL_DIR"
        fi
    fi
    COMPOSE_FILE_PATH="${INSTALL_DIR}/${COMPOSE_FILE_NAME}"

    if [ -z "$COMPOSE_CMD" ]; then
        detect_user_ids_and_engine
    fi

    if [ -d "${INSTALL_DIR}" ]; then
        if [ -n "$COMPOSE_CMD" ] && [ -f "${COMPOSE_FILE_PATH}" ]; then
            info "info_stopping_containers"
            (cd "${INSTALL_DIR}" && ${COMPOSE_CMD} down -v --remove-orphans) || warn "warn_stop_container_failed"
        fi
        local confirm_delete
        local delete_prompt
        delete_prompt=$(get_string "prompt_delete_config_data" "${INSTALL_DIR}")
        read -r -p "$(echo -e "${YELLOW}${delete_prompt}${NC}")" confirm_delete
        if [[ "$confirm_delete" == "y" || "$confirm_delete" == "Y" ]]; then
            info "info_deleting_dir" "${INSTALL_DIR}"
            rm -rf "${INSTALL_DIR}"
            info "info_dir_deleted" "${INSTALL_DIR}"
        else
            info "info_keep_dir" "${INSTALL_DIR}"
        fi
    else
        info "info_install_dir_not_found" "${INSTALL_DIR}"
    fi

    local bashrc_file="${HOME}/.bashrc"
    local helper_script_file="${HOME}/.bash_caddy_f2b_helper"
    local source_line="if [ -f ${helper_script_file} ]; then . ${helper_script_file}; fi"

    if [ -f "${helper_script_file}" ]; then
        info "info_deleting_helper_script" "${helper_script_file}"
        rm -f "${helper_script_file}"
        info "info_helper_script_deleted"
    fi
    if [ -f "$bashrc_file" ] && grep -qF -- "$source_line" "$bashrc_file"; then
        info "info_removing_bashrc_ref" "${bashrc_file}"
        sed -i.bak "\_^\s*if \[ -f ${helper_script_file} \]; then \. ${helper_script_file}; fi\$_d" "$bashrc_file"
        sed -i.bak '/^$/N;/^\n$/D' "$bashrc_file" # Remove blank lines that may result
        info "info_bashrc_ref_removed" "$bashrc_file" "$bashrc_file"
        info "info_run_source_bashrc"
    fi
    info "info_uninstall_complete"
}

# --- 脚本入口 ---
main() {
    # 尽早进行语言选择，以便后续所有输出都是国际化的
    # 但 TEXTS 和 get_string 等函数必须先定义
    select_language

    case "$1" in
        uninstall)
            uninstall_services
            ;;
        help|--help|-h)
            echo -e "$(get_string "main_help_title")"
            echo ""
            echo -e "$(get_string "main_help_usage" "$(basename "$0")")"
            echo ""
            echo -e "$(get_string "main_help_available_commands")"
            echo -e "$(get_string "main_help_cmd_default")"
            echo -e "$(get_string "main_help_cmd_uninstall")"
            echo -e "$(get_string "main_help_cmd_help")"
            echo ""
            echo -e "$(get_string "main_help_default_install_dir" "$DEFAULT_INSTALL_DIR")"
            echo -e "$(get_string "main_help_source_bashrc")"
            echo -e "$(get_string "main_help_f2b_test_rules")"
            ;;
        test)
            warn "main_help_legacy_test_cmd_warn"
            info "main_help_legacy_test_cmd_info"
            info "main_help_legacy_test_cmd_alt_source"
            ;;
        *)
            if [ -n "$1" ]; then
                warn "main_help_unknown_command" "$1"
                main "help" # 递归调用以显示帮助
                exit 1
            fi
            main_install
            ;;
    esac
}

# 确保 TEXTS 和核心函数已定义，再调用 main
main "$@"

exit 0
