#!/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}"
COMPOSE_FILE="${COMPOSE_FILE:-${ROOT_DIR}/compose.yaml}"
APP_SERVICE="${APP_SERVICE:-laravel.test}"
DB_SERVICE="${DB_SERVICE:-mysql}"
COMPOSER_IMAGE="${COMPOSER_IMAGE:-composer:2}"
BUILD_IMAGES=1
WARM_CACHE=1
PROJECT_UID=""
PROJECT_GID=""

usage() {
    cat <<'EOF'
Использование:
  ./scripts/deploy-ubuntu.sh [--skip-build] [--skip-cache]

Что делает скрипт:
  1. Создаёт .env из .env.example, если файла ещё нет
  2. Подготавливает storage/bootstrap cache директории
  3. Собирает docker-образ приложения
  4. Устанавливает production-зависимости Composer внутри контейнера
  5. Генерирует APP_KEY, если он пустой
  6. Поднимает контейнеры
  7. Ждёт готовности MySQL
  8. Запускает миграции и прогревает Laravel-кэш

Требования:
  - Ubuntu с установленными Docker Engine и Docker Compose Plugin
  - настроенный .env с корректными значениями APP_URL и AZEROTHCORE_AUTH_DB_*
EOF
}

log() {
    printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}

fail() {
    printf '[error] %s\n' "$*" >&2
    exit 1
}

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

env_value() {
    local key="$1"

    [[ -f "$ENV_FILE" ]] || return 0

    grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- | sed -e 's/^"//' -e 's/"$//'
}

set_env_value() {
    local key="$1"
    local value="$2"

    if grep -qE "^${key}=" "$ENV_FILE"; then
        sed -i "s#^${key}=.*#${key}=${value}#" "$ENV_FILE"
    else
        printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
    fi
}

compose() {
    docker compose -f "$COMPOSE_FILE" "$@"
}

composer_install() {
    docker run --rm \
        -u "${PROJECT_UID}:${PROJECT_GID}" \
        -e HOME=/tmp/composer-home \
        -e COMPOSER_HOME=/tmp/composer-home \
        -v "${ROOT_DIR}:/app" \
        -w /app \
        "$COMPOSER_IMAGE" \
        sh -lc 'mkdir -p "$COMPOSER_HOME" /app/vendor && git config --global --add safe.directory /app && composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction'
}

app_exec() {
    compose exec -T --user "${WWWUSER}:${WWWGROUP}" "$APP_SERVICE" "$@"
}

wait_for_mysql() {
    local container_id
    local status
    local attempts=0
    local max_attempts=60

    container_id="$(compose ps -q "$DB_SERVICE")"
    [[ -n "$container_id" ]] || fail "Не удалось определить контейнер MySQL"

    while (( attempts < max_attempts )); do
        status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_id" 2>/dev/null || true)"

        if [[ "$status" == "healthy" || "$status" == "running" ]]; then
            log "MySQL готов"
            return 0
        fi

        attempts=$((attempts + 1))
        sleep 2
    done

    fail "MySQL не перешёл в готовое состояние"
}

prepare_env() {
    if [[ -f "$ENV_FILE" ]]; then
        return 0
    fi

    [[ -f "${ROOT_DIR}/.env.example" ]] || fail "Не найден .env.example"

    cp "${ROOT_DIR}/.env.example" "$ENV_FILE"
    set_env_value "APP_ENV" "production"
    set_env_value "APP_DEBUG" "false"

    log "Создан ${ENV_FILE} из .env.example"
    log "Проверь APP_URL и AZEROTHCORE_AUTH_DB_* перед публичным запуском"
}

prepare_directories() {
    mkdir -p \
        "${ROOT_DIR}/bootstrap/cache" \
        "${ROOT_DIR}/storage/app" \
        "${ROOT_DIR}/storage/framework/cache/data" \
        "${ROOT_DIR}/storage/framework/sessions" \
        "${ROOT_DIR}/storage/framework/testing" \
        "${ROOT_DIR}/storage/framework/views" \
        "${ROOT_DIR}/storage/logs" \
        "${ROOT_DIR}/vendor"

    chmod -R ug+rwx "${ROOT_DIR}/bootstrap/cache" "${ROOT_DIR}/storage"

    if [[ "$(id -u)" -eq 0 ]]; then
        chown -R "${WWWUSER}:${WWWGROUP}" "${ROOT_DIR}/bootstrap/cache" "${ROOT_DIR}/storage"
    fi
}

resolve_ids() {
    PROJECT_UID="$(stat -c '%u' "$ROOT_DIR")"
    PROJECT_GID="$(stat -c '%g' "$ROOT_DIR")"
}

parse_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --skip-build)
                BUILD_IMAGES=0
                ;;
            --skip-cache)
                WARM_CACHE=0
                ;;
            -h|--help)
                usage
                exit 0
                ;;
            *)
                fail "Неизвестный аргумент: $1"
                ;;
        esac

        shift
    done
}

main() {
    parse_args "$@"

    require_command docker
    docker compose version >/dev/null 2>&1 || fail "Не найден Docker Compose Plugin"

    cd "$ROOT_DIR"

    prepare_env

    WWWUSER="$(env_value WWWUSER)"
    WWWGROUP="$(env_value WWWGROUP)"
    WWWUSER="${WWWUSER:-1000}"
    WWWGROUP="${WWWGROUP:-1000}"
    resolve_ids
    prepare_directories

    log "Устанавливаю production-зависимости Composer"
    composer_install

    if (( BUILD_IMAGES )); then
        log "Собираю образ приложения"
        compose build "$APP_SERVICE"
    fi

    if [[ -z "$(env_value APP_KEY)" ]]; then
        log "Генерирую APP_KEY"
        compose run --rm --no-deps --user "${WWWUSER}:${WWWGROUP}" --entrypoint php "$APP_SERVICE" artisan key:generate --force
    fi

    log "Поднимаю контейнеры"
    compose up -d

    wait_for_mysql

    log "Выполняю миграции"
    app_exec php artisan migrate --force

    log "Настраиваю Passport"
    if [[ ! -f "${ROOT_DIR}/storage/oauth-private.key" ]]; then
        app_exec php artisan passport:keys --force
        log "Сгенерированы ключи Passport"
    fi

    chmod 600 "${ROOT_DIR}/storage/oauth-private.key" "${ROOT_DIR}/storage/oauth-public.key"
    log "Установлены права 600 на ключи Passport"

    if ! app_exec php artisan passport:client --personal --no-interaction 2>/dev/null | grep -q 'already exists'; then
        app_exec php artisan passport:client --personal --name="Launcher" --no-interaction
        log "Создан Personal Access Client «Launcher»"
    else
        log "Personal Access Client уже существует"
    fi

    if (( WARM_CACHE )); then
        log "Обновляю Laravel-кэш"
        app_exec php artisan optimize:clear
        app_exec php artisan config:cache
        app_exec php artisan route:cache
        app_exec php artisan view:cache
    fi

    log "Деплой завершён"
    log "Сайт доступен по адресу: $(env_value APP_URL)"
    log "Проверка контейнеров: docker compose -f ${COMPOSE_FILE} ps"
}

main "$@"
