#!/usr/bin/env bash

set -Eeuo pipefail

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
BACKUP_DIR="${BACKUP_DIR:-${ROOT_DIR}/storage/backups}"
MYSQL_CLIENT_IMAGE="${MYSQL_CLIENT_IMAGE:-mysql:8.4}"

usage() {
    cat <<'EOF'
Использование:
  ./scripts/db-backup.sh export [--target=all|app|auth|characters] [--out=DIR]
  ./scripts/db-backup.sh import <файл.sql[.gz]> --target=app|auth|characters
  ./scripts/db-backup.sh list

Опции:
  --env=PATH       путь к .env (по умолчанию ./.env)
  --target=NAME    какая БД: app (laravel), auth (acore_auth),
                   characters (acore_characters), all (по умолчанию для export)
  --out=DIR        куда писать дампы (по умолчанию storage/backups)
  --gzip           сжимать дамп gzip-ом (по умолчанию включено для export)
  --no-gzip        не сжимать
  --force          для import: не спрашивать подтверждение
  --create-db      перед импортом создать БД, если её нет
  -h, --help       показать справку

Переменные окружения:
  ENV_FILE, BACKUP_DIR        — переопределяют значения по умолчанию
  MYSQL_CLIENT_IMAGE          — образ для фолбэка (по умолчанию mysql:8.4)

Зависимости:
  Если на хосте установлены mysqldump/mysql — используются они.
  Иначе скрипт запускает их в одноразовом контейнере MYSQL_CLIENT_IMAGE.

Примеры:
  ./scripts/db-backup.sh export
  ./scripts/db-backup.sh export --target=auth --out=/tmp
  ./scripts/db-backup.sh import /tmp/acore_auth_2026-04-26.sql.gz --target=auth
  ./scripts/db-backup.sh import dump.sql.gz --target=app --create-db --force
EOF
}

log()  { printf '\033[1;34m[db-backup]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[db-backup]\033[0m %s\n' "$*" >&2; }
err()  { printf '\033[1;31m[db-backup]\033[0m %s\n' "$*" >&2; }

require_cmd() {
    command -v "$1" >/dev/null 2>&1 || { err "Не найдена команда: $1"; exit 1; }
}

# Безопасное чтение значения из .env (поддержка кавычек)
env_get() {
    local key="$1" file="$2"
    local line value
    line="$(grep -E "^${key}=" "$file" | tail -n1 || true)"
    [[ -z "$line" ]] && return 0
    value="${line#${key}=}"
    # снять обрамляющие кавычки
    if [[ "$value" =~ ^\".*\"$ ]] || [[ "$value" =~ ^\'.*\'$ ]]; then
        value="${value:1:${#value}-2}"
    fi
    printf '%s' "$value"
}

load_target() {
    local target="$1"
    DB_DOCKER_SERVICE=""
    DB_DOCKER_PORT=""

    case "$target" in
        app)
            DB_HOST="$(env_get DB_HOST "$ENV_FILE")"
            DB_PORT="$(env_get DB_PORT "$ENV_FILE")"
            DB_NAME="$(env_get DB_DATABASE "$ENV_FILE")"
            DB_USER="$(env_get DB_USERNAME "$ENV_FILE")"
            DB_PASS="$(env_get DB_PASSWORD "$ENV_FILE")"
            local fwd_port
            fwd_port="$(env_get FORWARD_DB_PORT "$ENV_FILE")"
            if [[ "$DB_HOST" == "mysql" ]] ||
               [[ "$DB_HOST" == "host.docker.internal" ]] ||
               [[ "$DB_HOST" == "127.0.0.1" && -n "$fwd_port" && "${DB_PORT:-3306}" == "$fwd_port" ]] ||
               [[ "$DB_HOST" == "localhost" && -n "$fwd_port" && "${DB_PORT:-3306}" == "$fwd_port" ]]; then
                DB_DOCKER_SERVICE="mysql"
                DB_DOCKER_PORT="3306"
            fi
            ;;
        auth)
            DB_HOST="$(env_get AZEROTHCORE_AUTH_DB_HOST "$ENV_FILE")"
            DB_PORT="$(env_get AZEROTHCORE_AUTH_DB_PORT "$ENV_FILE")"
            DB_NAME="$(env_get AZEROTHCORE_AUTH_DB_DATABASE "$ENV_FILE")"
            DB_USER="$(env_get AZEROTHCORE_AUTH_DB_USERNAME "$ENV_FILE")"
            DB_PASS="$(env_get AZEROTHCORE_AUTH_DB_PASSWORD "$ENV_FILE")"
            ;;
        characters)
            DB_HOST="$(env_get AZEROTHCORE_CHARACTERS_DB_HOST "$ENV_FILE")"
            DB_PORT="$(env_get AZEROTHCORE_CHARACTERS_DB_PORT "$ENV_FILE")"
            DB_NAME="$(env_get AZEROTHCORE_CHARACTERS_DB_DATABASE "$ENV_FILE")"
            DB_USER="$(env_get AZEROTHCORE_CHARACTERS_DB_USERNAME "$ENV_FILE")"
            DB_PASS="$(env_get AZEROTHCORE_CHARACTERS_DB_PASSWORD "$ENV_FILE")"
            ;;
        *)
            err "Неизвестная цель: $target"; exit 2;;
    esac

    # docker-only хосты заменяем на localhost при запуске вне контейнера
    if [[ "$DB_HOST" == "mysql" || "$DB_HOST" == "host.docker.internal" ]]; then
        local fwd
        fwd="$(env_get FORWARD_DB_PORT "$ENV_FILE")"
        if [[ -n "$fwd" && "$target" == "app" ]]; then
            DB_PORT="$fwd"
        fi
        DB_HOST="127.0.0.1"
    fi

    if [[ -z "$DB_NAME" || -z "$DB_USER" ]]; then
        err "Не удалось определить параметры подключения для target=$target"
        exit 1
    fi
}

have_native_mysql() {
    command -v mysqldump >/dev/null 2>&1 && command -v mysql >/dev/null 2>&1
}

# Запуск mysqldump/mysql либо на хосте, либо в одноразовом docker-контейнере.
# Стдин/стдаут пробрасываются — годится и для дампа, и для импорта по pipe.
# $1 — "mysqldump" или "mysql"; остальные — доп.аргументы тулзе.
# Подключение читается из глобалей DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME.
run_mysql_tool() {
    local tool="$1"; shift

    if have_native_mysql; then
        local defaults
        defaults="$(mktemp)"
        chmod 600 "$defaults"
        {
            printf '[client]\n'
            printf 'host=%s\n' "$DB_HOST"
            printf 'port=%s\n' "${DB_PORT:-3306}"
            printf 'user=%s\n' "$DB_USER"
            if [[ -n "${DB_PASS:-}" && "$DB_PASS" != "null" ]]; then
                printf 'password=%s\n' "$DB_PASS"
            fi
        } > "$defaults"
        local rc=0
        "$tool" --defaults-extra-file="$defaults" "$@" || rc=$?
        rm -f "$defaults"
        return $rc
    fi

    require_cmd docker

    if [[ -n "${DB_DOCKER_SERVICE:-}" ]]; then
        local pwd_value=""
        if [[ -n "${DB_PASS:-}" && "$DB_PASS" != "null" ]]; then
            pwd_value="$DB_PASS"
        fi

        docker compose exec -T \
            -e MYSQL_PWD="$pwd_value" \
            "$DB_DOCKER_SERVICE" \
            "$tool" \
                -h127.0.0.1 \
                -P"${DB_DOCKER_PORT:-3306}" \
                -u"$DB_USER" \
                "$@"
        return
    fi

    local target_host="$DB_HOST"
    if [[ "$target_host" == "127.0.0.1" || "$target_host" == "localhost" ]]; then
        target_host="host.docker.internal"
    fi

    local pwd_value=""
    if [[ -n "${DB_PASS:-}" && "$DB_PASS" != "null" ]]; then
        pwd_value="$DB_PASS"
    fi

    docker run --rm -i \
        --add-host=host.docker.internal:host-gateway \
        -e MYSQL_PWD="$pwd_value" \
        "$MYSQL_CLIENT_IMAGE" \
        "$tool" \
            -h"$target_host" \
            -P"${DB_PORT:-3306}" \
            -u"$DB_USER" \
            "$@"
}

do_export_one() {
    local target="$1" out_dir="$2" use_gzip="$3"
    load_target "$target"

    local ts dump_path
    ts="$(date +%Y-%m-%d_%H-%M-%S)"
    dump_path="${out_dir}/${DB_NAME}_${ts}.sql"

    log "Экспорт ${target} (${DB_USER}@${DB_HOST}:${DB_PORT:-3306}/${DB_NAME}, env=${ENV_FILE}) → ${dump_path}$([[ $use_gzip -eq 1 ]] && echo .gz)"

    local -a dump_args=(
        --single-transaction
        --quick
        --routines
        --triggers
        --events
        --set-gtid-purged=OFF
        --hex-blob
        --default-character-set=utf8mb4
        --no-tablespaces
        "$DB_NAME"
    )

    if [[ "$use_gzip" -eq 1 ]]; then
        require_cmd gzip
        run_mysql_tool mysqldump "${dump_args[@]}" | gzip -c > "${dump_path}.gz"
        log "Готово: ${dump_path}.gz ($(du -h "${dump_path}.gz" | cut -f1))"
    else
        run_mysql_tool mysqldump "${dump_args[@]}" > "$dump_path"
        log "Готово: ${dump_path} ($(du -h "$dump_path" | cut -f1))"
    fi
}

do_export() {
    local target="$1" out_dir="$2" use_gzip="$3"
    mkdir -p "$out_dir"
    if [[ "$target" == "all" ]]; then
        for t in app auth characters; do
            do_export_one "$t" "$out_dir" "$use_gzip"
        done
    else
        do_export_one "$target" "$out_dir" "$use_gzip"
    fi
}

do_import() {
    local file="$1" target="$2" force="$3" create_db="$4"
    [[ -f "$file" ]] || { err "Файл не найден: $file"; exit 1; }
    [[ -z "$target" ]] && { err "Для import обязателен --target"; exit 2; }

    load_target "$target"

    if [[ "$force" -ne 1 ]]; then
        printf '\033[1;33m[!] Импорт перезапишет данные в %s@%s:%s/%s (env=%s). Продолжить? [y/N]: \033[0m' \
            "$DB_USER" "$DB_HOST" "${DB_PORT:-3306}" "$DB_NAME" "$ENV_FILE"
        read -r answer
        [[ "$answer" =~ ^[yYдД]$ ]] || { warn "Отменено"; exit 0; }
    fi

    if [[ "$create_db" -eq 1 ]]; then
        log "Создаю БД ${DB_NAME} при отсутствии"
        local saved_name="$DB_NAME"
        DB_NAME=""
        run_mysql_tool mysql -e \
            "CREATE DATABASE IF NOT EXISTS \`$saved_name\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
        DB_NAME="$saved_name"
    fi

    log "Импорт ${file} → ${target} (${DB_USER}@${DB_HOST}:${DB_PORT:-3306}/${DB_NAME}, env=${ENV_FILE})"

    if [[ "$file" == *.gz ]]; then
        require_cmd gzip
        gzip -dc "$file" | run_mysql_tool mysql "$DB_NAME"
    else
        run_mysql_tool mysql "$DB_NAME" < "$file"
    fi

    log "Импорт завершён"
}

do_list() {
    if [[ ! -d "$BACKUP_DIR" ]]; then
        warn "Директория бэкапов отсутствует: $BACKUP_DIR"
        return 0
    fi
    log "Бэкапы в $BACKUP_DIR:"
    ls -lh "$BACKUP_DIR" 2>/dev/null | awk 'NR>1 {print "  " $9 "  (" $5 ", " $6, $7, $8 ")"}'
}

main() {
    [[ $# -lt 1 ]] && { usage; exit 2; }
    local cmd="$1"; shift

    local target="" out_dir="$BACKUP_DIR" use_gzip=1 force=0 create_db=0 import_file=""

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --env=*)     ENV_FILE="${1#*=}";;
            --target=*)  target="${1#*=}";;
            --out=*)     out_dir="${1#*=}";;
            --gzip)      use_gzip=1;;
            --no-gzip)   use_gzip=0;;
            --force)     force=1;;
            --create-db) create_db=1;;
            -h|--help)   usage; exit 0;;
            -*)          err "Неизвестная опция: $1"; usage; exit 2;;
            *)           import_file="$1";;
        esac
        shift
    done

    [[ -f "$ENV_FILE" ]] || { err ".env не найден: $ENV_FILE"; exit 1; }

    case "$cmd" in
        export)
            [[ -z "$target" ]] && target="all"
            do_export "$target" "$out_dir" "$use_gzip"
            ;;
        import)
            [[ -z "$import_file" ]] && { err "Укажите путь к дампу"; usage; exit 2; }
            do_import "$import_file" "$target" "$force" "$create_db"
            ;;
        list)
            do_list
            ;;
        -h|--help|help)
            usage
            ;;
        *)
            err "Неизвестная команда: $cmd"
            usage
            exit 2
            ;;
    esac
}

main "$@"
