#!/usr/bin/env bash
#
# Gitea 远程备份脚本
# Author: System Administrator
# Version: 1.0.0
# License: MIT
#
# 功能描述：通过SSH远程执行Gitea备份操作，并将备份文件同步到本地
# 依赖要求：ssh, rsync, docker, date, grep, awk 等基础工具

set -euo pipefail
IFS=$'\n\t'

################################################################################
# 全局常量定义区
################################################################################

readonly REMOTE_PORT="22333"
readonly REMOTE_HOST="t0"
readonly SCRIPT_DIR="/root/wdd/backup"
readonly REMOTE_GITEA_CONTAINER="gitea-gitea-1"
readonly REMOTE_GITEA_CONFIG="/bitnami/gitea/custom/conf/app.ini"
readonly REMOTE_BACKUP_SOURCE="/data/gitea/gitea_data/data/tmp/gitea-dump-*.zip"
readonly LOCAL_BACKUP_TARGET="/data/t0_150_230_198_103/gitea/"

# > 日志配置
readonly LOG_DIR="${SCRIPT_DIR}/logs"
readonly LOG_FILE="${LOG_DIR}/gitea_backup_$(date +%Y%m%d).log"

# 日志级别常量
readonly LOG_LEVEL_DEBUG=0
readonly LOG_LEVEL_INFO=1
readonly LOG_LEVEL_WARN=2
readonly LOG_LEVEL_ERROR=3

# 当前日志级别（默认INFO）
CURRENT_LOG_LEVEL=${LOG_LEVEL_INFO}

################################################################################
# 函数声明区
################################################################################

# 输出格式化日志信息（同时输出到控制台和日志文件）
# @param level string 日志级别（DEBUG/INFO/WARN/ERROR）
# @param message string 日志消息内容
# @return void
# @require CURRENT_LOG_LEVEL, LOG_FILE
log_message() {
  local level="$1"
  local message="$2"
  local timestamp
  timestamp=$(date '+%Y-%m-%d %H:%M:%S')
  local log_entry=""
  case "$level" in
  "DEBUG")
    if [ "${CURRENT_LOG_LEVEL}" -le ${LOG_LEVEL_DEBUG} ]; then
      log_entry="[DEBUG][${timestamp}] ${message}"
      echo "${log_entry}"
      echo "${log_entry}" >> "${LOG_FILE}"
    fi
    ;;
  "INFO")
    if [ "${CURRENT_LOG_LEVEL}" -le ${LOG_LEVEL_INFO} ]; then
      log_entry="[INFO][${timestamp}] ${message}"
      echo "${log_entry}"
      echo "${log_entry}" >> "${LOG_FILE}"
    fi
    ;;
  "WARN")
    if [ "${CURRENT_LOG_LEVEL}" -le ${LOG_LEVEL_WARN} ]; then
      log_entry="[WARN][${timestamp}] ${message}"
      echo "${log_entry}" >&2
      echo "${log_entry}" >> "${LOG_FILE}"
    fi
    ;;
  "ERROR")
    if [ "${CURRENT_LOG_LEVEL}" -le ${LOG_LEVEL_ERROR} ]; then
      log_entry="[ERROR][${timestamp}] ${message}"
      echo "${log_entry}" >&2
      echo "${log_entry}" >> "${LOG_FILE}"
    fi
    ;;
  *)
    log_entry="[UNKNOWN][${timestamp}] ${message}"
    echo "${log_entry}" >&2
    echo "${log_entry}" >> "${LOG_FILE}"
    ;;
  esac
}

###
# 执行远程SSH命令
# @param command string 需要执行的远程命令
# @return int 命令执行退出码
# @require REMOTE_HOST, REMOTE_PORT
execute_remote_command() {
  local command="$1"
  local exit_code

  log_message "DEBUG" "执行远程命令: ${command}"

  # > 通过SSH连接到远程主机执行命令
  ssh -p "${REMOTE_PORT}" "${REMOTE_HOST}" "${command}"
  exit_code=$?

  if [ ${exit_code} -ne 0 ]; then
    log_message "ERROR" "远程命令执行失败，退出码: ${exit_code}"
    return ${exit_code}
  fi

  return 0
}

###
# 执行Gitea备份操作
# @return int 操作执行状态码
# @require REMOTE_GITEA_CONTAINER, REMOTE_GITEA_CONFIG
perform_gitea_backup() {
  local backup_command="docker exec -i ${REMOTE_GITEA_CONTAINER} /opt/bitnami/gitea/bin/gitea dump -c ${REMOTE_GITEA_CONFIG}"

  log_message "INFO" "开始执行Gitea备份..."

  # > 执行Gitea dump命令生成备份文件
  if ! execute_remote_command "${backup_command}"; then
    log_message "ERROR" "Gitea备份命令执行失败"
    return 1
  fi

  log_message "INFO" "Gitea备份命令执行成功"
  return 0
}

###
# 重命名备份文件（添加时间戳）
# @return int 操作执行状态码
# @require REMOTE_GITEA_CONTAINER
rename_backup_file() {
  local rename_command="docker exec -i ${REMOTE_GITEA_CONTAINER} /bin/sh -c \"mv /opt/bitnami/gitea/gitea-dump-*.zip /opt/bitnami/gitea/data/tmp/gitea-dump-\$(date +%Y%m%d-%H%M%S).zip\""

  log_message "INFO" "开始重命名备份文件..."

  # > 在容器内重命名备份文件，添加时间戳
  if ! execute_remote_command "${rename_command}"; then
    log_message "ERROR" "备份文件重命名失败"
    return 1
  fi

  log_message "INFO" "备份文件重命名成功"
  return 0
}

###
# 同步备份文件到本地
# @return int 操作执行状态码
# @require REMOTE_HOST, REMOTE_PORT, REMOTE_BACKUP_SOURCE, LOCAL_BACKUP_TARGET
sync_backup_to_local() {
  log_message "INFO" "开始同步备份文件到本地..."

  # > 创建本地目标目录（如果不存在）
  if [ ! -d "${LOCAL_BACKUP_TARGET}" ]; then
    mkdir -p "${LOCAL_BACKUP_TARGET}"
    log_message "DEBUG" "创建本地目录: ${LOCAL_BACKUP_TARGET}"
  fi

  # > 使用rsync同步文件，保留关键属性
  rsync -avz -e "ssh -p ${REMOTE_PORT}" \
    "${REMOTE_HOST}:${REMOTE_BACKUP_SOURCE}" \
    "${LOCAL_BACKUP_TARGET}"

  local exit_code=$?
  if [ ${exit_code} -ne 0 ]; then
    log_message "ERROR" "rsync同步失败，退出码: ${exit_code}"
    return ${exit_code}
  fi

  log_message "INFO" "备份文件同步成功"
  return 0
}

###
# 清理远程备份文件
# @return int 操作执行状态码
# @require REMOTE_BACKUP_SOURCE
cleanup_remote_backup() {
  local cleanup_command="rm -f ${REMOTE_BACKUP_SOURCE}"

  log_message "INFO" "开始清理远程备份文件..."

  # > 删除远程主机上的临时备份文件
  if ! execute_remote_command "${cleanup_command}"; then
    log_message "ERROR" "远程备份文件清理失败"
    return 1
  fi

  log_message "INFO" "远程备份文件清理成功"
  return 0
}

###
# 主执行函数 - 协调整个备份流程
# @return int 脚本执行最终状态码
main() {
  local overall_success=true

  log_message "INFO" "=== Gitea备份流程开始 ==="

  # 切换到工作目录
  cd "${SCRIPT_DIR}" || {
    log_message "ERROR" "无法切换到工作目录: ${SCRIPT_DIR}"
    return 1
  }

  # 执行备份流程
  if ! perform_gitea_backup; then
    overall_success=false
  fi

  if ! rename_backup_file; then
    overall_success=false
  fi

  if ! sync_backup_to_local; then
    overall_success=false
  fi

  if ! cleanup_remote_backup; then
    overall_success=false
  fi

  # 汇总执行结果
  if [ "${overall_success}" = true ]; then
    log_message "INFO" "=== Gitea备份流程完成 ==="
    return 0
  else
    log_message "ERROR" "=== Gitea备份流程部分失败 ==="
    return 1
  fi
}

################################################################################
# 异常处理设置
################################################################################

# 设置trap捕获信号
trap 'log_message "ERROR" "脚本被中断"; exit 1' INT TERM

################################################################################
# 主执行流程
################################################################################

# 函数调用关系：
# main -> perform_gitea_backup -> execute_remote_command
#        -> rename_backup_file -> execute_remote_command
#        -> sync_backup_to_local
#        -> cleanup_remote_backup -> execute_remote_command

# 执行主函数
if main; then
  log_message "INFO" "脚本执行成功"
  exit 0
else
  log_message "ERROR" "脚本执行失败"
  exit 1
fi
