function envelopeResponses(_req, res, next) {
    const originalJson = res.json.bind(res);

    res.json = (payload) => {
        if (isApiEnvelope(payload)) {
            return originalJson(payload);
        }

        const isError = res.statusCode >= 400;
        const message = extractMessage(payload, isError);
        const errors = extractErrors(payload, isError, message);
        const data = isError ? null : payload;

        return originalJson({
            status: isError ? "error" : "success",
            data,
            message,
            errors,
        });
    };

    next();
}

function isApiEnvelope(payload) {
    return Boolean(
        payload &&
        typeof payload === "object" &&
        !Array.isArray(payload) &&
        Object.prototype.hasOwnProperty.call(payload, "status") &&
        Object.prototype.hasOwnProperty.call(payload, "data") &&
        Object.prototype.hasOwnProperty.call(payload, "message") &&
        Object.prototype.hasOwnProperty.call(payload, "errors")
    );
}

function extractMessage(payload, isError) {
    if (payload && typeof payload === "object" && !Array.isArray(payload)) {
        if (typeof payload.message === "string" && payload.message.length) {
            return payload.message;
        }
        if (typeof payload.error === "string" && payload.error.length) {
            return payload.error;
        }
    }

    return isError ? "Request failed" : "OK";
}

function extractErrors(payload, isError, message) {
    if (!isError) return [];
    if (payload && typeof payload === "object" && !Array.isArray(payload)) {
        if (Array.isArray(payload.errors)) {
            return payload.errors;
        }
        if (payload.error) {
            return [payload.error];
        }
    }
    return message ? [message] : [];
}

module.exports = {
    envelopeResponses,
};
