#!/usr/bin/env bash
set -Eeuo pipefail

REPOSITORY="zxyszx/NewSzxcn-Email"
RAW_BASE="https://raw.githubusercontent.com/${REPOSITORY}/main"
INSTALL_DIR="${LANQIN_INSTALL_DIR:-/opt/newszxcn-email}"
COMMAND="${1:-install}"
ROLLBACK_FILE="${INSTALL_DIR}/.rollback-image"

log() { printf '\033[1;34m[NewSzxcn]\033[0m %s\n' "$*"; }
success() { printf '\033[1;32m[完成]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[提示]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m[错误]\033[0m %s\n' "$*" >&2; exit 1; }

usage() {
  cat <<'EOF'
NewSzxcn Email 管理命令

用法：newszxcn-email <command>

  install     首次安装或修复部署
  update      备份数据库并更新到最新版
  status      查看容器与健康状态
  logs        持续查看运行日志
  rollback    回滚到上次命令行更新前的镜像
  uninstall   停止并移除容器，保留邮件与配置
EOF
}

require_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    fail "请使用 root 运行，例如：curl -fsSL ${RAW_BASE}/install.sh | sudo bash"
  fi
}

require_curl() {
  command -v curl >/dev/null 2>&1 || fail "系统缺少 curl，请先安装 curl。"
}

ensure_docker() {
  if ! command -v docker >/dev/null 2>&1; then
    log "未检测到 Docker，正在安装 Docker Engine..."
    curl -fsSL https://get.docker.com | sh
  fi
  if command -v systemctl >/dev/null 2>&1; then
    systemctl enable --now docker >/dev/null 2>&1 || true
  fi
  docker compose version >/dev/null 2>&1 || fail "需要 Docker Compose v2。"
}

compose() {
  docker compose --project-directory "${INSTALL_DIR}" -f "${INSTALL_DIR}/docker-compose.yml" "$@"
}

script_dir() {
  cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd
}

refresh_assets() {
  local source_dir
  source_dir="$(script_dir || true)"
  install -d -m 0755 "${INSTALL_DIR}"
  if [[ -f "${source_dir}/deploy/docker-compose.yml" && -f "${source_dir}/deploy/.env.example" ]]; then
    install -m 0644 "${source_dir}/deploy/docker-compose.yml" "${INSTALL_DIR}/docker-compose.yml"
    install -m 0644 "${source_dir}/deploy/.env.example" "${INSTALL_DIR}/.env.example"
    install -m 0755 "${source_dir}/install.sh" /usr/local/bin/newszxcn-email
  else
    curl -fsSL "${RAW_BASE}/deploy/docker-compose.yml" -o "${INSTALL_DIR}/docker-compose.yml"
    curl -fsSL "${RAW_BASE}/deploy/.env.example" -o "${INSTALL_DIR}/.env.example"
    curl -fsSL "${RAW_BASE}/install.sh" -o /usr/local/bin/newszxcn-email.new
    chmod 0755 /usr/local/bin/newszxcn-email.new
    mv /usr/local/bin/newszxcn-email.new /usr/local/bin/newszxcn-email
  fi
}

random_secret() {
  if command -v openssl >/dev/null 2>&1; then
    openssl rand -hex 24
  else
    od -An -N24 -tx1 /dev/urandom | tr -d ' \n'
  fi
}

set_env() {
  local key="$1" value="$2" file="${INSTALL_DIR}/.env" tmp
  tmp="$(mktemp)"
  awk -v key="${key}" -v value="${value}" '
    BEGIN { found=0 }
    $0 ~ "^" key "=" { print key "=" value; found=1; next }
    { print }
    END { if (!found) print key "=" value }
  ' "${file}" > "${tmp}"
  cat "${tmp}" > "${file}"
  rm -f "${tmp}"
}

env_value() {
  local key="$1"
  sed -n "s/^${key}=//p" "${INSTALL_DIR}/.env" | tail -n 1
}

prompt_value() {
  local variable="$1" prompt="$2" default_value="$3" secret="${4:-false}"
  local value="${!variable:-}"
  if [[ -z "${value}" && -r /dev/tty ]]; then
    if [[ "${secret}" == "true" ]]; then
      read -r -s -p "${prompt}${default_value:+ [自动生成]}: " value </dev/tty
      printf '\n' >/dev/tty
    else
      read -r -p "${prompt}${default_value:+ [${default_value}]}: " value </dev/tty
    fi
  fi
  value="${value:-${default_value}}"
  printf '%s' "${value}"
}

configure_first_install() {
  if [[ -f "${INSTALL_DIR}/.env" ]]; then
    return
  fi
  install -m 0600 "${INSTALL_DIR}/.env.example" "${INSTALL_DIR}/.env"

  local hostname public_url admin_email admin_password update_token
  hostname="$(prompt_value LANQIN_PUBLIC_HOSTNAME "邮件服务器域名，例如 mail.example.com" "")"
  [[ "${hostname}" =~ ^[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]] || fail "邮件服务器域名格式不正确。"
  public_url="$(prompt_value LANQIN_PUBLIC_BASE_URL "Webmail 访问地址" "https://${hostname}")"
  admin_email="$(prompt_value LANQIN_ADMIN_EMAIL "初始管理员邮箱" "admin@${hostname#mail.}")"
  [[ "${admin_email}" == *@*.* ]] || fail "管理员邮箱格式不正确。"
  admin_password="$(prompt_value LANQIN_ADMIN_PASSWORD "初始管理员密码" "" true)"
  if [[ -z "${admin_password}" ]]; then
    admin_password="$(random_secret)"
    warn "已自动生成管理员密码：${admin_password}"
  fi
  [[ ${#admin_password} -ge 10 ]] || fail "管理员密码至少需要 10 个字符。"
  update_token="$(random_secret)"

  set_env LANQIN_PUBLIC_HOSTNAME "${hostname}"
  set_env LANQIN_PUBLIC_BASE_URL "${public_url}"
  set_env LANQIN_ADMIN_EMAIL "${admin_email}"
  set_env LANQIN_ADMIN_PASSWORD "${admin_password}"
  set_env LANQIN_UPDATE_TOKEN "${update_token}"
  chmod 0600 "${INSTALL_DIR}/.env"
}

ensure_update_token() {
  local token
  token="$(env_value LANQIN_UPDATE_TOKEN || true)"
  if [[ -z "${token}" ]]; then
    set_env LANQIN_UPDATE_TOKEN "$(random_secret)"
    chmod 0600 "${INSTALL_DIR}/.env"
  fi
}

prepare_directories() {
  install -d -m 0755 "${INSTALL_DIR}/data" "${INSTALL_DIR}/mail" "${INSTALL_DIR}/dkim"
  install -d -m 0700 "${INSTALL_DIR}/data/backups"
}

wait_for_health() {
  local attempts="${1:-60}"
  for ((i=1; i<=attempts; i++)); do
    if curl -fsS --max-time 3 http://127.0.0.1/healthz >/dev/null 2>&1; then
      return 0
    fi
    sleep 2
  done
  return 1
}

backup_database() {
  local timestamp
  timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
  if [[ -n "$(compose ps -q lanqin-email 2>/dev/null || true)" ]]; then
    compose exec -T lanqin-email sh -c "mkdir -p /data/backups && sqlite3 /data/lanqin.db \".backup '/data/backups/cli-update-${timestamp}.db'\"" >/dev/null
    log "数据库已备份到 data/backups/cli-update-${timestamp}.db"
  fi
}

remember_current_image() {
  local container_id image_id rollback_tag
  container_id="$(compose ps -q lanqin-email 2>/dev/null || true)"
  [[ -n "${container_id}" ]] || return 0
  image_id="$(docker inspect --format '{{.Image}}' "${container_id}")"
  rollback_tag="newszxcn-email:rollback-$(date -u +%Y%m%d%H%M%S)"
  docker image tag "${image_id}" "${rollback_tag}"
  printf '%s\n' "${rollback_tag}" > "${ROLLBACK_FILE}"
}

do_install() {
  ensure_docker
  refresh_assets
  configure_first_install
  ensure_update_token
  prepare_directories
  log "正在拉取 NewSzxcn Email 镜像..."
  compose pull
  log "正在启动服务..."
  compose up -d --remove-orphans
  wait_for_health 90 || fail "服务未能通过健康检查，请执行 newszxcn-email logs 查看日志。"
  success "安装完成：$(env_value LANQIN_PUBLIC_BASE_URL)"
  warn "下一步请配置 MX、SPF、DKIM、DMARC，并确认 25/465/587/993/995 端口可访问。"
}

do_update() {
  [[ -f "${INSTALL_DIR}/.env" ]] || fail "尚未安装，请先执行 install。"
  ensure_docker
  refresh_assets
  ensure_update_token
  backup_database
  remember_current_image
  log "正在拉取最新版..."
  compose pull
  compose up -d --remove-orphans
  if ! wait_for_health 90; then
    warn "新版本健康检查失败，正在自动回滚。"
    do_rollback
    fail "更新失败，已回滚到原镜像。"
  fi
  success "系统已更新，配置、邮件和数据库均已保留。"
}

do_rollback() {
  [[ -f "${ROLLBACK_FILE}" ]] || fail "没有可用的回滚镜像。"
  local image
  image="$(tr -d '\r\n' < "${ROLLBACK_FILE}")"
  docker image inspect "${image}" >/dev/null 2>&1 || fail "回滚镜像已不存在：${image}"
  log "正在回滚到 ${image}..."
  LANQIN_IMAGE="${image}" compose up -d --no-deps --force-recreate lanqin-email
  wait_for_health 90 || fail "回滚后服务仍未通过健康检查，请查看日志。"
  success "已回滚到 ${image}。"
}

do_status() {
  [[ -f "${INSTALL_DIR}/docker-compose.yml" ]] || fail "尚未安装。"
  compose ps
  if curl -fsS --max-time 3 http://127.0.0.1/healthz >/dev/null 2>&1; then
    success "Web 与 API 健康检查正常。"
  else
    fail "健康检查失败。"
  fi
}

do_uninstall() {
  [[ -f "${INSTALL_DIR}/docker-compose.yml" ]] || fail "尚未安装。"
  compose down --remove-orphans
  success "容器已移除，${INSTALL_DIR} 中的配置、邮件和数据库仍然保留。"
}

require_root
require_curl
case "${COMMAND}" in
  install) do_install ;;
  update) do_update ;;
  status) ensure_docker; do_status ;;
  logs) ensure_docker; compose logs -f --tail=200 lanqin-email updater ;;
  rollback) ensure_docker; do_rollback ;;
  uninstall) ensure_docker; do_uninstall ;;
  help|-h|--help) usage ;;
  *) usage; fail "未知命令：${COMMAND}" ;;
esac
