#!/usr/bin/env bash

set -Eeuo pipefail
umask 022

DEPLOY_LOG="${HOME}/deploy-ot.log"
REPOPATH="/home/neurapyc/repositories/vp_ot"
DEPLOYPATH="/home/neurapyc/public_html/ot.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)" "$*"
}

fail() {
    log "FAILED: $*"
    exit 1
}

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

    fail "Composer not found on deployment host"
}

resolve_npm() {
    local candidate

    for candidate in \
        /opt/cpanel/ea-nodejs22/bin/npm \
        /opt/cpanel/ea-nodejs21/bin/npm \
        /opt/cpanel/ea-nodejs20/bin/npm \
        /opt/alt/alt-nodejs22/root/usr/bin/npm \
        /opt/alt/alt-nodejs21/root/usr/bin/npm \
        /opt/alt/alt-nodejs20/root/usr/bin/npm
    do
        if [ -x "$candidate" ]; then
            NPM_BIN="$candidate"
            return
        fi
    done

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

    NPM_BIN=""
}

resolve_node() {
    local candidate

    for candidate in \
        /opt/cpanel/ea-nodejs22/bin/node \
        /opt/cpanel/ea-nodejs21/bin/node \
        /opt/cpanel/ea-nodejs20/bin/node \
        /opt/alt/alt-nodejs22/root/usr/bin/node \
        /opt/alt/alt-nodejs21/root/usr/bin/node \
        /opt/alt/alt-nodejs20/root/usr/bin/node
    do
        if [ -x "$candidate" ]; then
            NODE_BIN="$candidate"
            NODE_VERSION="$("$NODE_BIN" -v 2>/dev/null || true)"
            return
        fi
    done

    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

    # Vite 6 supports Node ^18 || ^20 || >=22. Accept any 22.x and 20.19+.
    if [ "$major" -eq 22 ]; then
        return 0
    fi

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

    return 1
}

create_directories() {
    mkdir -p "$DEPLOYPATH"
    chmod 755 "$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"

    if [ -d "$DEPLOYPATH/public" ]; then
        chmod 755 "$DEPLOYPATH/public"
    fi
}

sync_application() {
    rsync -a \
        --chmod=D755,F644 \
        --delete-delay \
        --timeout=60 \
        --partial \
        --exclude=".env" \
        --exclude="vendor/" \
        --exclude="storage/" \
        --exclude="bootstrap/cache/" \
        --exclude="node_modules/" \
        --exclude=".git/" \
        "$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"
        # Put the resolved Node first on PATH so npm/vite don't pick up an older
        # system node (which lacks the Web Crypto global Vite needs).
        export PATH="$(dirname "$NODE_BIN"):$PATH"
        maybe_timeout 1800 "$NPM_BIN" ci
    )
}

build_frontend_assets() {
    (
        cd "$DEPLOYPATH"
        export PATH="$(dirname "$NODE_BIN"):$PATH"
        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 755 "$DEPLOYPATH"
    chmod -R 775 "$DEPLOYPATH/storage" "$DEPLOYPATH/bootstrap/cache"

    if [ -f "$DEPLOYPATH/.htaccess" ]; then
        chmod 644 "$DEPLOYPATH/.htaccess"
    fi

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

        if [ -f "$DEPLOYPATH/public/.htaccess" ]; then
            chmod 644 "$DEPLOYPATH/public/.htaccess"
        fi
    fi
}

refresh_laravel() {
    (
        cd "$DEPLOYPATH"

        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
    )
}

publish_capabilities() {
    # Best-effort: push this app's role->capability catalog to the IdP for central
    # display. A failure (IdP unreachable, etc.) must never fail the deploy.
    (
        cd "$DEPLOYPATH"
        maybe_timeout 60 php artisan neurapy:publish-capabilities || true
    )
}

log "Starting deployment for ot"
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

if [ -f "$DEPLOYPATH/package.json" ] && [ ! -f "$DEPLOYPATH/public/build/manifest.json" ]; then
    resolve_node

    if [ -n "${NODE_VERSION}" ] && ! node_version_is_supported "${NODE_VERSION}"; then
        fail "Node.js ${NODE_VERSION} is too old for the Vite build and no committed frontend build artifacts exist at public/build/manifest.json"
    fi

    if [ -z "${NODE_BIN}" ]; then
        fail "Node.js is not available and no committed frontend build artifacts exist at public/build/manifest.json"
    fi
fi

run_step "Resolve Composer" resolve_composer
run_step "Resolve npm" resolve_npm
run_step "Resolve Node.js" resolve_node
log "Composer binary: ${COMPOSER_BIN}"
log "npm binary: ${NPM_BIN:-not found}"
log "Node.js binary: ${NODE_BIN:-not found}"
log "Node.js version: ${NODE_VERSION:-not found}"
run_step "Install PHP dependencies" install_php_dependencies

if [ -f "$DEPLOYPATH/package.json" ]; then
    # Prefer a fresh server-side build so the deployed assets can never be stale.
    # If the build fails (e.g. Node quirk), fall back to committed artifacts rather
    # than breaking the whole deploy. Fail only when there's no usable build at all.
    if [ -n "${NPM_BIN}" ] && [ -n "${NODE_BIN}" ] && node_version_is_supported "${NODE_VERSION}"; then
        log "Building frontend on server with Node ${NODE_VERSION} (${NPM_BIN})"
        if install_frontend_dependencies && build_frontend_assets; then
            cleanup_frontend_dependencies || true
            log "Frontend built on server."
        elif [ -f "$DEPLOYPATH/public/build/manifest.json" ]; then
            cleanup_frontend_dependencies || true
            log "WARNING: server build failed — falling back to committed build artifacts. Commit a fresh public/build."
        else
            fail "Frontend build failed and no committed public/build/manifest.json to fall back to"
        fi
    elif [ -f "$DEPLOYPATH/public/build/manifest.json" ]; then
        log "Node unavailable/too old — using committed build artifacts at public/build/manifest.json"
    else
        fail "Frontend build cannot continue without a supported Node.js version or committed public/build/manifest.json"
    fi
fi

run_step "Run database migrations" run_migrations
run_step "Apply filesystem permissions" apply_permissions
run_step "Refresh Laravel caches" refresh_laravel
run_step "Publish capability catalog to IdP" publish_capabilities

log "Deployment completed"
