#!/usr/bin/env bash
#
# install-xui.sh — развёртывание 3x-ui в Docker + nginx reverse proxy + SSL (certbot dns-cloudflare)
#
# Создаёт:
#   /System/docker/xui/compose.yaml
#   /System/data/xui/{db,cert}
#   /etc/nginx/sites-available/<домен>
#   символьную ссылку в /etc/nginx/sites-enabled/
#
# При любой ошибке на этапе nginx/certbot — полный откат изменений в nginx.
#
set -euo pipefail

DOCKER_DIR="/System/docker/xui"
DATA_DIR="/System/data/xui"
COMPOSE_FILE="${DOCKER_DIR}/compose.yaml"
CF_CREDENTIALS="${CF_CREDENTIALS:-$HOME/.secrets/mrirbis.ini}"
UPSTREAM_PORT="2053"

# --- Состояние для отката --------------------------------------------------
ROLLBACK_NEEDED=0
NGINX_AVAILABLE=""
NGINX_ENABLED=""
BACKUP_FILE=""
CREATED_AVAILABLE=0
CREATED_ENABLED=0

log()  { echo -e "\n\033[1;32m==>\033[0m $*"; }
warn() { echo -e "\033[1;33m[!]\033[0m $*" >&2; }
err()  { echo -e "\033[1;31m[ОШИБКА]\033[0m $*" >&2; }

rollback() {
  [[ ${ROLLBACK_NEEDED} -eq 0 ]] && return 0
  warn "Выполняется откат изменений в nginx..."

  # Убираем симлинк, если создали его мы
  if [[ ${CREATED_ENABLED} -eq 1 && -L "${NGINX_ENABLED}" ]]; then
    rm -f "${NGINX_ENABLED}"
    echo "    удалён ${NGINX_ENABLED}"
  fi

  # Восстанавливаем или удаляем конфиг
  if [[ -n "${BACKUP_FILE}" && -f "${BACKUP_FILE}" ]]; then
    mv -f "${BACKUP_FILE}" "${NGINX_AVAILABLE}"
    echo "    восстановлен прежний ${NGINX_AVAILABLE}"
  elif [[ ${CREATED_AVAILABLE} -eq 1 && -f "${NGINX_AVAILABLE}" ]]; then
    rm -f "${NGINX_AVAILABLE}"
    echo "    удалён ${NGINX_AVAILABLE}"
  fi

  # Пробуем вернуть nginx в рабочее состояние
  if nginx -t &>/dev/null; then
    systemctl reload nginx &>/dev/null || true
    echo "    nginx возвращён в исходное состояние"
  else
    err "nginx всё ещё не проходит проверку конфигурации — проверь вручную: nginx -t"
  fi
}
trap rollback ERR INT TERM

# --- Проверки --------------------------------------------------------------
[[ $EUID -ne 0 ]] && { err "Запусти от root: sudo bash $0"; exit 1; }

for cmd in docker nginx certbot; do
  command -v "$cmd" &>/dev/null || { err "Не найдена команда '$cmd'. Установи её и повтори."; exit 1; }
done

docker compose version &>/dev/null || { err "Не найден плагин docker compose."; exit 1; }

# --- Запрос домена ---------------------------------------------------------
read -rp "Введи домен для 3x-ui (например xui.1rb1.me): " DOMAIN
DOMAIN="$(echo "${DOMAIN}" | tr -d '[:space:]')"

if [[ -z "${DOMAIN}" || ! "${DOMAIN}" =~ ^[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$ ]]; then
  err "Некорректный домен: '${DOMAIN}'"
  exit 1
fi

NGINX_AVAILABLE="/etc/nginx/sites-available/${DOMAIN}"
NGINX_ENABLED="/etc/nginx/sites-enabled/${DOMAIN}"

if [[ ! -f "${CF_CREDENTIALS}" ]]; then
  err "Не найден файл с ключами Cloudflare: ${CF_CREDENTIALS}"
  err "Создай его или задай путь: CF_CREDENTIALS=/path/to/file.ini bash $0"
  exit 1
fi
chmod 600 "${CF_CREDENTIALS}"

echo
echo "  Домен:        ${DOMAIN}"
echo "  Compose:      ${COMPOSE_FILE}"
echo "  Данные:       ${DATA_DIR}"
echo "  Cloudflare:   ${CF_CREDENTIALS}"
read -rp $'\nПродолжить? [y/N]: ' CONFIRM
[[ "${CONFIRM}" =~ ^[YyДд]$ ]] || { echo "Отменено."; exit 0; }

# --- Каталоги --------------------------------------------------------------
log "Создание каталогов"
mkdir -p "${DOCKER_DIR}" "${DATA_DIR}/db" "${DATA_DIR}/cert"

# --- compose.yaml ----------------------------------------------------------
log "Создание ${COMPOSE_FILE}"
if [[ -f "${COMPOSE_FILE}" ]]; then
  cp -a "${COMPOSE_FILE}" "${COMPOSE_FILE}.bak.$(date +%Y%m%d-%H%M%S)"
  warn "Существующий compose-файл сохранён как .bak"
fi

cat > "${COMPOSE_FILE}" << 'EOF'
services:
  3x-ui:
    image: ghcr.io/mhsanaei/3x-ui:latest
    container_name: 3x-ui
    restart: unless-stopped
    network_mode: host
    volumes:
      - /System/data/xui/db:/etc/x-ui
      - /System/data/xui/cert:/root/cert
    environment:
      - XRAY_VMESS_AEAD_FORCED=false
EOF

# --- Запуск контейнера -----------------------------------------------------
log "Запуск контейнера 3x-ui"
docker compose -f "${COMPOSE_FILE}" pull
docker compose -f "${COMPOSE_FILE}" up -d
docker compose -f "${COMPOSE_FILE}" ps

# --- Конфиг nginx ----------------------------------------------------------
# С этого момента изменения обратимы — включаем откат.
ROLLBACK_NEEDED=1

log "Создание конфига nginx: ${NGINX_AVAILABLE}"
if [[ -f "${NGINX_AVAILABLE}" ]]; then
  BACKUP_FILE="${NGINX_AVAILABLE}.bak.$(date +%Y%m%d-%H%M%S)"
  cp -a "${NGINX_AVAILABLE}" "${BACKUP_FILE}"
  warn "Существующий конфиг сохранён: ${BACKUP_FILE}"
else
  CREATED_AVAILABLE=1
fi

cat > "${NGINX_AVAILABLE}" << EOF
server {
    listen 80;
    listen [::]:80;
    server_name ${DOMAIN};

    # Enforce HTTPS
    return 301 https://\$server_name:443\$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name ${DOMAIN};

    ssl_certificate     /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;

    location / {
        proxy_pass http://localhost:${UPSTREAM_PORT};
        proxy_set_header Host               \$host;
        proxy_set_header X-Real-IP          \$remote_addr;
        proxy_set_header X-Forwarded-For    \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto  \$scheme;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade            \$http_upgrade;
        proxy_set_header Connection         "Upgrade";
    }
}
EOF

# --- Сертификат ------------------------------------------------------------
log "Получение сертификата Let's Encrypt для ${DOMAIN}"
if [[ -d "/etc/letsencrypt/live/${DOMAIN}" ]]; then
  warn "Сертификат для ${DOMAIN} уже существует — пропускаю выпуск."
else
  certbot certonly \
    --dns-cloudflare \
    --dns-cloudflare-credentials "${CF_CREDENTIALS}" \
    --preferred-challenges dns-01 \
    -d "${DOMAIN}" \
    --non-interactive --agree-tos --register-unsafely-without-email
fi

if [[ ! -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]]; then
  err "Сертификат не найден после выпуска — прерываю."
  exit 1
fi

# --- Активация сайта -------------------------------------------------------
log "Активация сайта в nginx"
if [[ -e "${NGINX_ENABLED}" ]]; then
  warn "${NGINX_ENABLED} уже существует — оставляю как есть."
else
  ln -s "${NGINX_AVAILABLE}" "${NGINX_ENABLED}"
  CREATED_ENABLED=1
fi

# --- Проверка и перезапуск -------------------------------------------------
log "Проверка конфигурации nginx"
if ! nginx -t; then
  err "Конфигурация nginx содержит ошибки (см. вывод выше)."
  exit 1
fi

log "Перезапуск nginx"
systemctl reload nginx || systemctl restart nginx

# Всё прошло успешно — откат больше не нужен
ROLLBACK_NEEDED=0
trap - ERR INT TERM

echo
echo "────────────────────────────────────────────────"
echo " Готово."
echo " Панель:    https://${DOMAIN}"
echo " Логин по умолчанию: admin / admin — смени сразу!"
echo " Логи:      docker logs -f 3x-ui"
echo " Управление: docker compose -f ${COMPOSE_FILE} [up -d|down|restart]"
echo "────────────────────────────────────────────────"
