#!/bin/bash
set -euo pipefail

# Usage: ./deploy-release.sh <release_tag> <target_dir>
# This script is intended to be executed ON THE REMOTE SERVER (OVH) via SSH

if [ "$#" -ne 2 ]; then
    echo "Usage: $0 <release_tag> <target_dir>"
    exit 1
fi

RELEASE_TAG=$1
TARGET_DIR="${2%/}"

# 1. Strictly validate release tag format BEFORE constructing or using any paths
if ! [[ "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    echo "Error: Release tag must match SemVer format (e.g., v1.0.0)."
    exit 1
fi

RELEASES_ROOT=$(readlink -m "${TARGET_DIR}/releases")
RELEASE_PATH="${RELEASES_ROOT}/${RELEASE_TAG}"
CURRENT_TARGET="releases/${RELEASE_TAG}"
ARCHIVE_PATH="${TARGET_DIR}/release_${RELEASE_TAG}.tar.gz"
STAGING_PATH="${TARGET_DIR}/releases/.${RELEASE_TAG}.staging.$$"

# `current` must always be absent or a relative symlink with the exact
# releases/vX.Y.Z format. This text form is required by Apache on OVH.
# Canonicalisation remains a defence in depth check before activation.
CURRENT_RELEASE_PATH=""
validate_current_topology() {
    local current_link_target

    if [ -L "${TARGET_DIR}/current" ]; then
        current_link_target=$(readlink "${TARGET_DIR}/current")

        if ! [[ "${current_link_target}" =~ ^releases/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            echo "Error: ${TARGET_DIR}/current must be a relative symlink in the form releases/vX.Y.Z (found: ${current_link_target}). Aborting before activation."
            exit 1
        fi

        CURRENT_RELEASE_PATH=$(readlink -m "${TARGET_DIR}/${current_link_target}")
        if [ "${CURRENT_RELEASE_PATH}" != "${RELEASES_ROOT}/${current_link_target#releases/}" ]; then
            echo "Error: ${TARGET_DIR}/current points outside ${RELEASES_ROOT} (${current_link_target}). Aborting before activation."
            exit 1
        fi

        return
    fi

    if [ -e "${TARGET_DIR}/current" ]; then
        echo "Error: ${TARGET_DIR}/current exists as a real file or directory (must be a symlink or absent). Aborting before activation."
        exit 1
    fi
}

verify_current_target() {
    local current_link_target
    current_link_target=$(readlink "${TARGET_DIR}/current")

    if [ "${current_link_target}" != "${CURRENT_TARGET}" ]; then
        echo "Error: ${TARGET_DIR}/current must point exactly to ${CURRENT_TARGET} after activation (found: ${current_link_target})."
        exit 1
    fi
}

validate_current_topology

# Trap cleanup to guarantee no partial staging state or orphan symlink left on failure
cleanup() {
    local exit_code=$?
    if [ -d "${STAGING_PATH:-}" ]; then
        rm -rf "${STAGING_PATH}"
    fi
    if [ -L "${TARGET_DIR:-}/current.new" ] || [ -f "${TARGET_DIR:-}/current.new" ]; then
        rm -f "${TARGET_DIR}/current.new"
    fi
    exit "${exit_code}"
}
trap cleanup EXIT INT TERM

echo "Starting deployment process for ${RELEASE_TAG}..."

# 2. Check if target release already exists (Resume vs Immutable Active vs Corrupt)
if [ -d "${RELEASE_PATH}" ]; then
    # Case 1: Release is already active in current symlink
    if [ -L "${TARGET_DIR}/current" ] && [ "${CURRENT_RELEASE_PATH}" = "${RELEASE_PATH}" ]; then
        echo "Error: Release ${RELEASE_TAG} is already active and immutable. Cannot redeploy."
        exit 1
    fi

    # Case 2: Release exists on disk but is not active in current (Resume activation scenario)
    echo "Existing release directory detected at ${RELEASE_PATH}. Checking for resume readiness..."

    # Must contain the .release-ready marker demonstrating migrations and checks passed
    if [ ! -f "${RELEASE_PATH}/.release-ready" ]; then
        echo "Error: Existing release ${RELEASE_PATH} is missing .release-ready marker. Release is incomplete or unverified. Manual investigation required (will not overwrite/delete automatically)."
        exit 1
    fi

    # Structural integrity check
    if [ ! -f "${RELEASE_PATH}/vendor/autoload.php" ]; then
        echo "Error: ${RELEASE_PATH}/vendor/autoload.php is missing in existing release."
        exit 1
    fi
    if [ ! -d "${RELEASE_PATH}/public/build" ]; then
        echo "Error: ${RELEASE_PATH}/public/build is missing in existing release."
        exit 1
    fi
    if [ ! -f "${RELEASE_PATH}/public/index.php" ]; then
        echo "Error: ${RELEASE_PATH}/public/index.php is missing in existing release."
        exit 1
    fi

    # Shared targets must exist; a textually correct but broken symlink is invalid.
    if [ ! -f "${TARGET_DIR}/shared/.env" ]; then
        echo "Error: ${TARGET_DIR}/shared/.env is missing for resumed activation."
        exit 1
    fi
    if [ ! -d "${TARGET_DIR}/shared/storage" ]; then
        echo "Error: ${TARGET_DIR}/shared/storage is missing for resumed activation."
        exit 1
    fi
    if [ ! -d "${TARGET_DIR}/shared/storage/app/public" ]; then
        echo "Error: ${TARGET_DIR}/shared/storage/app/public is missing for resumed activation."
        exit 1
    fi

    # Shared symlinks verification
    ENV_LINK=$(readlink "${RELEASE_PATH}/.env" 2>/dev/null || true)
    if [ ! -L "${RELEASE_PATH}/.env" ] || [ "${ENV_LINK}" != "${TARGET_DIR}/shared/.env" ]; then
        echo "Error: ${RELEASE_PATH}/.env is not a valid symlink to ${TARGET_DIR}/shared/.env"
        exit 1
    fi

    STORAGE_LINK=$(readlink "${RELEASE_PATH}/storage" 2>/dev/null || true)
    if [ ! -L "${RELEASE_PATH}/storage" ] || [ "${STORAGE_LINK}" != "${TARGET_DIR}/shared/storage" ]; then
        echo "Error: ${RELEASE_PATH}/storage is not a valid symlink to ${TARGET_DIR}/shared/storage"
        exit 1
    fi

    PUB_STORAGE_LINK=$(readlink "${RELEASE_PATH}/public/storage" 2>/dev/null || true)
    if [ ! -L "${RELEASE_PATH}/public/storage" ] || [ "${PUB_STORAGE_LINK}" != "${TARGET_DIR}/shared/storage/app/public" ]; then
        echo "Error: ${RELEASE_PATH}/public/storage is not a valid symlink to ${TARGET_DIR}/shared/storage/app/public"
        exit 1
    fi

    echo "Resuming activation of existing finalized release ${RELEASE_TAG} (skipping extraction and migrations)..."
    ln -sfn "${CURRENT_TARGET}" "${TARGET_DIR}/current.new"

    if ! mv -Tf "${TARGET_DIR}/current.new" "${TARGET_DIR}/current"; then
        echo "Error: mv -Tf failed during resumed activation."
        exit 1
    fi
    verify_current_target
    echo "Switched to ${RELEASE_PATH} atomically using mv -Tf (resumed activation)."

    rm -f "${ARCHIVE_PATH}"
    echo "Deployment of ${RELEASE_TAG} successful. Current release is now live."
    exit 0
fi

# 3. Preflight environment & prerequisites checks
echo "Running environment preflight checks..."
if [ ! -f "${TARGET_DIR}/shared/.env" ]; then
    echo "Error: ${TARGET_DIR}/shared/.env is missing."
    exit 1
fi

if ! command -v php >/dev/null 2>&1; then
    echo "Error: PHP CLI is not available on this system."
    exit 1
fi

if [ ! -f "${ARCHIVE_PATH}" ]; then
    echo "Error: Archive ${ARCHIVE_PATH} not found!"
    exit 1
fi

# 4. Ensure architecture directories exist
mkdir -p "${TARGET_DIR}/releases" \
         "${TARGET_DIR}/shared/storage/app/public" \
         "${TARGET_DIR}/shared/storage/framework/cache/data" \
         "${TARGET_DIR}/shared/storage/framework/sessions" \
         "${TARGET_DIR}/shared/storage/framework/views" \
         "${TARGET_DIR}/shared/storage/logs"

# 5. Extract release archive into isolated staging directory
echo "Extracting release to staging directory (${STAGING_PATH})..."
mkdir -p "${STAGING_PATH}"
tar -xzf "${ARCHIVE_PATH}" -C "${STAGING_PATH}"

# 6. Preflight release artifact checks in staging
echo "Running release artifact preflight checks..."
if [ ! -f "${STAGING_PATH}/vendor/autoload.php" ]; then
    echo "Error: vendor/autoload.php is missing in release artifact."
    exit 1
fi
if [ ! -d "${STAGING_PATH}/public/build" ]; then
    echo "Error: public/build is missing in release artifact."
    exit 1
fi
if [ ! -f "${STAGING_PATH}/public/index.php" ]; then
    echo "Error: public/index.php is missing in release artifact."
    exit 1
fi
echo "Artifact preflight checks passed."

# 7. Clean up default storage and link shared components in staging
rm -rf "${STAGING_PATH}/storage"
ln -sfn "${TARGET_DIR}/shared/storage" "${STAGING_PATH}/storage"

rm -f "${STAGING_PATH}/.env"
ln -sfn "${TARGET_DIR}/shared/.env" "${STAGING_PATH}/.env"

# Ensure public/storage symlink points to shared/storage/app/public
ln -sfn "${TARGET_DIR}/shared/storage/app/public" "${STAGING_PATH}/public/storage"

# 8. Execute database migrations in staging environment BEFORE activation
echo "Running database migrations..."
php "${STAGING_PATH}/artisan" migrate --force
echo "Database migrations completed successfully."

# 9. Mark release as verified and ready for activation
touch "${STAGING_PATH}/.release-ready"

# 10. Finalize immutable release directory
mv "${STAGING_PATH}" "${RELEASE_PATH}"
echo "Release directory finalized: ${RELEASE_PATH}"

# 11. Atomic switch of current symlink. The text target is intentionally
# relative because OVH Apache rejects the absolute-current topology.
ln -sfn "${CURRENT_TARGET}" "${TARGET_DIR}/current.new"

if ! mv -Tf "${TARGET_DIR}/current.new" "${TARGET_DIR}/current"; then
    echo "Error: mv -Tf failed on this system."
    exit 1
fi
verify_current_target
echo "Switched to ${RELEASE_PATH} atomically using mv -Tf."

# 12. Clean up release archive
rm -f "${ARCHIVE_PATH}"

echo "Deployment of ${RELEASE_TAG} successful. Current release is now live."
