<?php
/**
 * whp-ajax-endpoint.php — one-call wrapper for async mutation endpoints.
 *
 * Standardizes the boilerplate that api/database-ajax.php hand-rolled today
 * (output buffering, bootstrap, auth gate, CSRF gate, action dispatch, JSON
 * envelope) so each api/<feature>-ajax.php collapses to ~4 lines and every
 * endpoint behaves identically. The handler closure does the manager
 * construction and returns the SAME shape the page handlers already produce:
 *   ['message' => string, 'type' => string]   (type 'success' => success:true)
 *
 * This mirrors api/database-ajax.php exactly — it does NOT invent a new
 * convention; it just factors the existing one out:
 *   - error_reporting/display_errors hardening + ob_start() at the very top so
 *     stray notices/warnings can't corrupt the JSON body
 *   - require_once('../auto-prepend.php') for the standard security bootstrap
 *     (defines AUTH_USER, validate_csrf_token(), sessions, etc.)
 *   - 401 {success:false,message:'Not authenticated'} when AUTH_USER is unset
 *   - 403 {success:false,message:'Invalid CSRF token'} when validate_csrf_token()
 *     (zero args) fails
 *   - calls $handler() (which constructs its manager + requires its libs) and
 *     emits {success:(type==='success'), message:(message ?: 'Done.')}
 *
 * The matching client is js/whp-async-forms.js, which posts with the
 * 'X-WHP-Async: 1' header and reads {success, message}.
 *
 * Usage:
 *   // api/<feature>-ajax.php
 *   require_once __DIR__ . '/../includes/whp-ajax-endpoint.php';
 *   require_once __DIR__ . '/../libs/<feature>_actions.php';
 *   whp_ajax_endpoint(function () {
 *       $M = new <Mgr>();
 *       return whp_<feature>_handle_action($M);   // ['message'=>..,'type'=>..]
 *   });
 *
 * Note: the handler requires its own libs/manager. Lib requires that need the
 * security bootstrap (MYSQL_PASS, NONCE_SALT, …) are safe because the wrapper
 * has already loaded auto-prepend.php before the closure runs.
 */

// Match database-ajax.php's hardening precisely: log, never echo, buffer early.
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ob_start();

require_once(__DIR__ . '/../auto-prepend.php');

if (!function_exists('whp_ajax_endpoint')) {

    /**
     * Emit the {success,message} JSON envelope and stop. Clears any buffered
     * stray output first so the body is always clean JSON.
     */
    function whp_ajax_endpoint_json(bool $success, string $message, int $status = 200, string $type = ''): void {
        if (ob_get_length() !== false) { @ob_end_clean(); }
        http_response_code($status);
        header('Content-Type: application/json');
        $out = ['success' => $success, 'message' => $message];
        if ($type !== '') { $out['type'] = $type; }   // raw severity so the client colours the toast
        echo json_encode($out);
        exit();
    }

    /**
     * Run a standardized authenticated + CSRF-checked async mutation endpoint.
     *
     * @param callable $handler Returns ['message'=>string, 'type'=>string].
     *                          'type' === 'success' maps to success:true; any
     *                          other type maps to success:false.
     */
    function whp_ajax_endpoint(callable $handler): void {
        if (!defined('AUTH_USER')) {
            whp_ajax_endpoint_json(false, 'Not authenticated', 401);
        }
        if (!function_exists('validate_csrf_token') || !validate_csrf_token()) {
            whp_ajax_endpoint_json(false, 'Invalid CSRF token', 403);
        }

        $r = $handler();
        $msg  = (string) ($r['message'] ?? '');
        $type = (string) ($r['type'] ?? '');
        // 'success'/'warning'/'info' all mean "the action completed — refresh the
        // UI"; only 'danger' (or an empty no-op type) is a failure. The raw type
        // is passed through so the client can colour the toast accordingly
        // (e.g. a partial-success 'warning' shows amber, not red, and still
        // triggers the post-success DOM update).
        $isOk = in_array($type, ['success', 'warning', 'info'], true);
        whp_ajax_endpoint_json($isOk, $msg !== '' ? $msg : 'Done.', 200, $type);
    }
}
