#!/usr/bin/env bash
# Auto-deploy: детект изменений через git diff, сборка и деплой только изменённых компонентов.
#
# Зависимости: bash, git, ssh; rsync (Linux/macOS) или tar (Git Bash).
# Конфиг: scripts/deploy.env.
#
# Примеры:
#   ./scripts/deploy-auto.sh           # деплой изменений
#   ./scripts/deploy-auto.sh --force   # полный деплой всех компонентов
#   ./scripts/deploy-auto.sh -f        # только фронт
#   ./scripts/deploy-auto.sh -b        # только бэкенд

set -euo pipefail

case "$(uname -s 2>/dev/null)" in
  MINGW* | MSYS*) export MSYS2_ARG_CONV_EXCL="*" ;;
esac

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

# --- Config ---
if [[ -f "$SCRIPT_DIR/deploy.env" ]]; then
  set -a
  source "$SCRIPT_DIR/deploy.env"
  set +a
fi

DEPLOY_HOST="${DEPLOY_HOST:-}"
DEPLOY_USER="${DEPLOY_USER:-root}"
DEPLOY_PATH="${DEPLOY_PATH:-/opt/craftshop}"
DEPLOY_FRONTEND_DIST="${DEPLOY_FRONTEND_DIST:-$DEPLOY_PATH/www}"
DEPLOY_SSH_IDENTITY="${DEPLOY_SSH_IDENTITY:-}"
DEPLOY_RESTART_CMD="${DEPLOY_RESTART_CMD:-systemctl restart craftshop-api}"
DEPLOY_SERVER_OWNER="${DEPLOY_SERVER_OWNER:-root}"
DEPLOY_SKIP_CHOWN="${DEPLOY_SKIP_CHOWN:-0}"

RSYNC_OPTS=(-az --delete --human-readable --progress)
SSH_OPTS=()
if [[ -n "${DEPLOY_SSH_IDENTITY}" ]]; then
  SSH_OPTS+=(-i "$DEPLOY_SSH_IDENTITY")
fi

REMOTE="${DEPLOY_USER}@${DEPLOY_HOST}"
SSH_BASE=(ssh "${SSH_OPTS[@]}" -o StrictHostKeyChecking=accept-new "$REMOTE")

# --- Flags ---
FORCE=false
TARGET="auto"

usage() {
  cat <<USAGE
Использование: $(basename "$0") [опции]

  --force        Деплой всех компонентов (игнорировать diff)
  --frontend-only | -f   Только клиент
  --backend-only  | -b   Только сервер
  --help | -h     Помощь

Окружение (или scripts/deploy.env):
  DEPLOY_HOST           хост SSH (обязательно)
  DEPLOY_PATH           корень приложения на сервере (по умолчанию: /opt/craftshop)
  DEPLOY_SSH_IDENTITY   путь к приватному ключу (-i ssh)
  DEPLOY_RESTART_CMD    команда рестарта API после деплоя бэкенда
USAGE
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --force) FORCE=true ;;
    --frontend-only | -f) TARGET="frontend" ;;
    --backend-only | -b) TARGET="backend" ;;
    -h | --help) usage; exit 0 ;;
    *) echo "Неизвестный аргумент: $1" >&2; usage >&2; exit 1 ;;
  esac
  shift
done

if [[ -z "$DEPLOY_HOST" ]]; then
  echo "Укажите DEPLOY_HOST (или добавьте в scripts/deploy.env)" >&2
  exit 1
fi

# --- Helpers ---
remote_exec() {
  "${SSH_BASE[@]}" "$@"
}

should_use_tar_transport() {
  case "$(uname -s 2>/dev/null)" in
    MINGW*|MSYS*|CYGWIN_NT*) return 0 ;;
    *) return 1 ;;
  esac
}

build_rsync_rsh() {
  printf '%q ' ssh "${SSH_OPTS[@]}" -o StrictHostKeyChecking=accept-new
}

# --- Diff detection ---
changed_client=false
changed_server=false

if [[ "$TARGET" == "frontend" ]]; then
  changed_client=true
elif [[ "$TARGET" == "backend" ]]; then
  changed_server=true
elif [[ "$FORCE" == true ]]; then
  changed_client=true
  changed_server=true
else
  DEPLOYED_FILE="$ROOT/.deployed-commit"
  if [[ -f "$DEPLOYED_FILE" ]]; then
    LAST_DEPLOYED=$(cat "$DEPLOYED_FILE")
  else
    LAST_DEPLOYED="HEAD~1"
  fi

  echo ">>> Diff: $LAST_DEPLOYED..HEAD"
  CHANGED_FILES=$(git -C "$ROOT" diff --name-only "$LAST_DEPLOYED" HEAD 2>/dev/null || true)

  if echo "$CHANGED_FILES" | grep -q "^client/"; then
    changed_client=true
  fi
  if echo "$CHANGED_FILES" | grep -q "^server/"; then
    changed_server=true
  fi
  if echo "$CHANGED_FILES" | grep -q "^shared/"; then
    changed_client=true
    changed_server=true
  fi

  if [[ "$changed_client" == false && "$changed_server" == false ]]; then
    echo ">>> Ничего не изменилось с последнего деплоя."
    exit 0
  fi
fi

echo ">>> Клиент: $changed_client, Сервер: $changed_server"

# --- Deploy: Client ---
if [[ "$changed_client" == true ]]; then
  echo ">>> Сборка клиента..."
  (cd "$ROOT/client" && npm install && npm run build)

  remote_exec mkdir -p "$DEPLOY_FRONTEND_DIST"

  if should_use_tar_transport; then
    remote_exec "find ${DEPLOY_FRONTEND_DIST} -mindepth 1 -delete 2>/dev/null || true"
    (cd "$ROOT/client/dist" && tar -czf - .) | \
      "${SSH_BASE[@]}" "mkdir -p ${DEPLOY_FRONTEND_DIST} && tar xzf - -C ${DEPLOY_FRONTEND_DIST}"
  else
    rsh="$(build_rsync_rsh)"
    rsync "${RSYNC_OPTS[@]}" -e "$rsh" \
      "$ROOT/client/dist/" "${REMOTE}:${DEPLOY_FRONTEND_DIST}/"
  fi

  echo ">>> Клиент задеплоен"
fi

# --- Deploy: Server ---
if [[ "$changed_server" == true ]]; then
  remote_exec mkdir -p "$DEPLOY_PATH/server" "$DEPLOY_PATH/shared"

  if should_use_tar_transport; then
    (cd "$ROOT/server" && tar -czf - \
      --exclude=node_modules --exclude=uploads --exclude=.git \
      --exclude='*.db' --exclude=.env --exclude=.dev_env \
      .) | "${SSH_BASE[@]}" "mkdir -p ${DEPLOY_PATH}/server && tar xzf - -C ${DEPLOY_PATH}/server"

    (cd "$ROOT/shared" && tar -czf - \
      --exclude=.git \
      .) | "${SSH_BASE[@]}" "mkdir -p ${DEPLOY_PATH}/shared && tar xzf - -C ${DEPLOY_PATH}/shared"
  else
    rsh="$(build_rsync_rsh)"
    rsync "${RSYNC_OPTS[@]}" -e "$rsh" \
      --exclude node_modules --exclude uploads --exclude .git \
      --exclude '*.db' --exclude .env --exclude .dev_env \
      "$ROOT/server/" "${REMOTE}:${DEPLOY_PATH}/server/"

    rsync "${RSYNC_OPTS[@]}" -e "$rsh" \
      --exclude .git \
      "$ROOT/shared/" "${REMOTE}:${DEPLOY_PATH}/shared/"
  fi

  echo ">>> Сервер: npm ci, prisma generate, migrate deploy"
  remote_exec bash -lc "set -e
    cd \"$DEPLOY_PATH/server\"
    npm ci
    npx prisma generate
    npx prisma migrate deploy
  "

  if [[ "${DEPLOY_USER}" == "root" && "${DEPLOY_SKIP_CHOWN}" != "1" ]]; then
    echo ">>> chown ${DEPLOY_SERVER_OWNER}"
    remote_exec chown -R "${DEPLOY_SERVER_OWNER}:${DEPLOY_SERVER_OWNER}" "$DEPLOY_PATH/server"
    remote_exec chown -R "${DEPLOY_SERVER_OWNER}:${DEPLOY_SERVER_OWNER}" "$DEPLOY_PATH/shared"
  fi

  if [[ -n "${DEPLOY_RESTART_CMD}" ]]; then
    echo ">>> Рестарт: $DEPLOY_RESTART_CMD"
    remote_exec bash -lc "$DEPLOY_RESTART_CMD"
  fi

  echo ">>> Сервер задеплоен"
fi

# --- Save deployed commit ---
if [[ "$TARGET" == "auto" && "$FORCE" != true ]]; then
  CURRENT_HEAD=$(git -C "$ROOT" rev-parse HEAD)
  echo "$CURRENT_HEAD" > "$ROOT/.deployed-commit"
  echo ">>> Сохранён коммит $CURRENT_HEAD"
fi

echo ">>> Готово."
