/**
 * api/lib/asyncHandler.js
 *
 * Higher-order function que envuelve una acción (async o sync) de Sails y
 * delega cualquier excepción a `res.domainError(err)`.
 *
 * Esto:
 *   1) Elimina el `try/catch + if (instanceof CustomError)` boilerplate.
 *   2) Centraliza el shape de respuesta vía la custom response.
 *   3) Evita el bug clásico de Sails con `instanceof` cuando módulos se
 *      recargan y la referencia de la clase queda desincronizada.
 *
 * Uso:
 *
 *   const asyncHandler = require('../lib/asyncHandler');
 *
 *   module.exports = {
 *     duplicate: asyncHandler(async (req, res) => {
 *       const result = await DuplicatedMenuService.duplicateMenu({ ... });
 *       return res.ok(result);
 *     })
 *   };
 *
 * Si la función interna lanza un error con `statusCode`, `res.domainError`
 * traduce a HTTP. Si lanza algo inesperado, también lo maneja (500 + log).
 */

'use strict';

module.exports = function asyncHandler(fn) {
    if (typeof fn !== 'function') {
        throw new TypeError('asyncHandler: se esperaba una función como argumento.');
    }

    return async function wrappedAction(req, res) {
        try {
            // `Promise.resolve` normaliza tanto sync como async, y propaga
            // throws sincrónicos al catch.
            return await Promise.resolve(fn.call(this, req, res));
        } catch (err) {
            // Si por alguna razón el response ya fue enviado (raro), no
            // intentamos re-responder — solo dejamos rastro.
            if (res && res.headersSent) {
                if (typeof sails !== 'undefined' && sails.log && sails.log.error) {
                    sails.log.error('[asyncHandler] Error tras headers enviados:', err);
                }
                return;
            }

            if (res && typeof res.domainError === 'function') {
                return res.domainError(err);
            }

            // Fallback defensivo: si el response personalizado no está cargado
            // (por ejemplo en tests aislados), usamos res.serverError o status 500.
            if (res && typeof res.serverError === 'function') {
                return res.serverError({
                    success: false,
                    message: err && err.message ? err.message : 'Error interno del servidor.'
                });
            }
            if (res && typeof res.status === 'function') {
                return res.status(err && typeof err.statusCode === 'number' ? err.statusCode : 500).json({
                    success: false,
                    message: err && err.message ? err.message : 'Error interno del servidor.'
                });
            }

            // Último recurso: re-throw para que el caller lo vea.
            throw err;
        }
    };
};
