#!/usr/bin/env bash

set -Eeuo pipefail

DEPLOY_LOG="${HOME}/deploy-mcq.log"
REPOPATH="/home/neurapyc/repositories/mcq"
DEPLOYPATH="/home/neurapyc/public_html/mcq.neurapy.com"

mkdir -p "$(dirname "$DEPLOY_LOG")"
exec > >(tee -a "$DEPLOY_LOG") 2>&1

timestamp() {
    date '+%Y-%m-%d %H:%M:%S'
}

log() {
    printf '[%s] %s\n' "$(timestamp)" "$*"
}

run_step() {
    local label="$1"
    shift

    log "START: ${label}"
    "$@"
    log "DONE: ${label}"
}

maybe_timeout() {
    local seconds="$1"
    shift

    if command -v timeout >/dev/null 2>&1; then
        timeout "${seconds}" "$@"
        return
    fi

    "$@"
}

on_error() {
    local exit_code="$?"

    log "FAILED: deployment stopped at line ${BASH_LINENO[0]:-unknown} with exit code ${exit_code}"
    exit "$exit_code"
}

trap on_error ERR

resolve_composer() {
    if command -v composer >/dev/null 2>&1; then
        COMPOSER_BIN="$(command -v composer)"
        return
    fi

    if [ -x /opt/cpanel/composer/bin/composer ]; then
        COMPOSER_BIN="/opt/cpanel/composer/bin/composer"
        return
    fi

    log "Composer not found on deployment host"
    exit 1
}

resolve_npm() {
    if command -v npm >/dev/null 2>&1; then
        NPM_BIN="$(command -v npm)"
        return
    fi

    NPM_BIN=""
}

resolve_node() {
    if command -v node >/dev/null 2>&1; then
        NODE_BIN="$(command -v node)"
        NODE_VERSION="$("$NODE_BIN" -v 2>/dev/null || true)"
        return
    fi

    NODE_BIN=""
    NODE_VERSION=""
}

node_version_is_supported() {
    local version="${1#v}"
    local major="${version%%.*}"
    local remainder="${version#*.}"
    local minor="${remainder%%.*}"

    if [ -z "$major" ] || [ -z "$minor" ]; then
        return 1
    fi

    if [ "$major" -gt 22 ]; then
        return 0
    fi

    if [ "$major" -eq 22 ] && [ "$minor" -ge 12 ]; then
        return 0
    fi

    if [ "$major" -eq 20 ] && [ "$minor" -ge 19 ]; then
        return 0
    fi

    return 1
}

create_directories() {
    mkdir -p "$DEPLOYPATH"
    mkdir -p \
        "$DEPLOYPATH/storage/app/public" \
        "$DEPLOYPATH/storage/framework/cache/data" \
        "$DEPLOYPATH/storage/framework/sessions" \
        "$DEPLOYPATH/storage/framework/views" \
        "$DEPLOYPATH/storage/logs" \
        "$DEPLOYPATH/bootstrap/cache"
}

sync_application() {
    rsync -a \
        --delete-delay \
        --timeout=60 \
        --partial \
        --exclude=".env" \
        --exclude="vendor/" \
        --exclude="storage/" \
        --exclude="bootstrap/cache/" \
        --exclude="node_modules/" \
        --exclude=".git/" \
        --exclude="browser-extension/" \
        "$REPOPATH/" "$DEPLOYPATH/"
}

install_php_dependencies() {
    (
        cd "$DEPLOYPATH"
        maybe_timeout 1800 env \
            COMPOSER_ALLOW_SUPERUSER=1 \
            COMPOSER_MEMORY_LIMIT=-1 \
            "$COMPOSER_BIN" install \
                --no-dev \
                --prefer-dist \
                --no-interaction \
                --no-progress \
                --optimize-autoloader
    )
}

install_frontend_dependencies() {
    (
        cd "$DEPLOYPATH"
        maybe_timeout 1800 "$NPM_BIN" ci
    )
}

build_frontend_assets() {
    (
        cd "$DEPLOYPATH"
        maybe_timeout 1800 "$NPM_BIN" run build
    )
}

cleanup_frontend_dependencies() {
    rm -rf "$DEPLOYPATH/node_modules"
}

run_migrations() {
    (
        cd "$DEPLOYPATH"
        maybe_timeout 600 php artisan migrate --force
    )
}

apply_permissions() {
    chmod -R 775 "$DEPLOYPATH/storage" "$DEPLOYPATH/bootstrap/cache"

    if [ -d "$DEPLOYPATH/public" ]; then
        find "$DEPLOYPATH/public" -type d -exec chmod 755 {} +
        find "$DEPLOYPATH/public" -type f -exec chmod 644 {} +
    fi

    # cPanel/suEXEC refuses to read a group- or world-writable .htaccess and
    # returns "Server unable to read htaccess file, denying access to be safe"
    # (a hard 403 before PHP even runs). Force every .htaccess — and the docroot
    # itself — to safe, readable permissions so a checkout with a permissive
    # umask can never take the site down.
    chmod 755 "$DEPLOYPATH" || true
    find "$DEPLOYPATH" -name '.htaccess' -type f -exec chmod 644 {} + || true
}

refresh_laravel() {
    (
        cd "$DEPLOYPATH"

        # Strip CRLF from .env before caching. A .env edited on Windows carries
        # a trailing \r on every value; config:cache bakes those into the cached
        # config and the app fails during bootstrap (before the logger exists) —
        # a 500 with nothing in the log. Normalising to LF here makes it safe.
        if [ -f .env ]; then
            sed -i 's/\r$//' .env
        fi

        rm -f bootstrap/cache/*.php
        maybe_timeout 120 php artisan storage:link || true
        maybe_timeout 180 php artisan config:cache
        maybe_timeout 180 php artisan route:cache
        maybe_timeout 180 php artisan view:cache
    )
}

log "Starting deployment for mcq"
log "Repository path: ${REPOPATH}"
log "Deploy path: ${DEPLOYPATH}"
log "Deploy log: ${DEPLOY_LOG}"

run_step "Prepare directories" create_directories
run_step "Sync application files" sync_application
# Normalise permissions immediately after the sync — BEFORE any step that can
# fail (composer, npm, migrations) — so the site can never be left serving an
# unreadable .htaccess if a later step aborts the deploy.
run_step "Apply filesystem permissions (post-sync)" apply_permissions
run_step "Resolve Composer" resolve_composer
run_step "Resolve npm" resolve_npm
run_step "Resolve Node.js" resolve_node
log "Composer binary: ${COMPOSER_BIN}"
run_step "Install PHP dependencies" install_php_dependencies

if [ -f "$DEPLOYPATH/package.json" ]; then
    if [ -n "${NPM_BIN}" ] && [ -n "${NODE_BIN}" ] && node_version_is_supported "${NODE_VERSION}"; then
        log "npm binary: ${NPM_BIN}"
        log "Node.js version: ${NODE_VERSION}"
        run_step "Install frontend dependencies" install_frontend_dependencies
        run_step "Build frontend assets" build_frontend_assets
        run_step "Clean frontend dependencies" cleanup_frontend_dependencies
    elif [ -f "$DEPLOYPATH/public/build/manifest.json" ]; then
        if [ -n "${NODE_VERSION}" ]; then
            log "Node.js ${NODE_VERSION} is too old for the Vite build on this server; using committed frontend build artifacts from repository"
        elif [ -n "${NPM_BIN}" ]; then
            log "npm was found but Node.js could not be resolved; using committed frontend build artifacts from repository"
        else
            log "npm not found; using committed frontend build artifacts from repository"
        fi
    else
        if [ -n "${NODE_VERSION}" ]; then
            log "Node.js ${NODE_VERSION} is too old for the Vite build and no frontend build artifacts exist at public/build/manifest.json"
        else
            log "npm/node not available and no frontend build artifacts exist at public/build/manifest.json"
        fi
        exit 1
    fi
fi

# Re-apply permissions after the build so freshly written assets (and the now
# installed vendor/ tree) get safe, readable modes too.
run_step "Apply filesystem permissions (post-build)" apply_permissions
run_step "Refresh Laravel caches" refresh_laravel

# Migrations run LAST: it is the riskiest step, and by this point the site is
# fully served with correct permissions and caches. A migration failure will be
# reported (non-zero exit) for investigation without taking the site offline.
run_step "Run database migrations" run_migrations

log "Deployment completed"
