Source: RouterEvents/eventTemplate/service.js

import { query, UUID2hex, HEX2uuid } from '@commtool/sql-query';
import { renderObject } from '../../utils/renderTemplates.js';
import { Templates } from '../../utils/compileTemplates.js';
import { getUID } from '../../utils/UUIDs.js';
import { queueAdd } from '../../tree/treeQueue/treeQueue.js';
import { publishEvent } from '../../utils/events.js';
import { diff } from 'deep-object-diff';

/**
 * Add visibility filters for event template
 * @param {Buffer} UID - Template UID
 * @param {Object} eventT - Template object with Data property
 * @param {Object} req - Request object for session data
 */
export const addVisibilityFilter = async (UID, eventT, req) => {
    // add the filters defining who can use the template and who can administer the template, as stored in the event template
    const [UIDs] = await query(`SELECT UIDV1() AS UIDuse, UIDV1() AS UIDadmin`, []);

    // add filter to orga for extraction of the usable objects and pointing to template
    const filterUse = { job: eventT.Data.templateVisibility };
    await query(
        `
        INSERT INTO ObjectBase(UID,Type,UIDBelongsTo,Title,Display,SortName, FullTextIndex, dindex,Data)
        VALUES (?,'visible',?,?,?,'visible','',0,?)
        `,
        [UIDs.UIDuse, UUID2hex(req.session.root), 'event Template usage', eventT.Display, JSON.stringify(filterUse)]
    );
    // now put the initial execution of the filters into the queue
    await queueAdd(UUID2hex(req.session.root), UUID2hex(req.session.user), 'visible', UIDs.UIDuse, UUID2hex(req.session.root), null, UID);

    // add filter to orga for extraction of the usable objects and pointing to template
    const filterAdmin = { job: eventT.Data.templateAdministration };
    await query(
        `
        INSERT INTO ObjectBase(UID,Type,UIDBelongsTo,Title,Display,SortName, FullTextIndex, dindex,Data)
        VALUES (?,'changeable',?,?,?,'changeable','',0,?)
        `,
        [UIDs.UIDadmin, UUID2hex(req.session.root), 'event Template admin', eventT.Display, JSON.stringify(filterAdmin)]
    );
    // now put the initial execution of the filter into the queue
    await queueAdd(UUID2hex(req.session.root), UUID2hex(req.session.user), 'changeable', UIDs.UIDadmin, UUID2hex(req.session.root), null, UID);
};

/**
 * Delete visibility filters for event template
 * @param {Buffer} UID - Template UID
 */
export const deleteVisibilityFilter = async (UID) => {
    // delete as well the visibility filter
    await query(
        `DELETE ObjectBase,Links  FROM ObjectBase
        LEFT JOIN Links ON (Links.UID=ObjectBase.UID AND Links.Type = 'list') 
        WHERE Links.UIDTarget =? AND ObjectBase.Type IN ('visible','changeable','admin')`,
        [UID]
    );
    // delete visibility to this object
    await query(`DELETE FROM Visible WHERE UID=?`, [UID]);
};

/**
 * Create or update an event template
 * @param {Object} bodyData - Request body data
 * @param {Object} session - User session
 * @returns {Promise<Object>} Result with success status and template data
 */
export const createOrUpdateEventTemplate = async (bodyData, session) => {
    const template = Templates[session.root].eventTemplate;
    const UID = await getUID({ session, body: bodyData });
    let oldData = null;

    if (bodyData.UID) {
        const previous = await query(`SELECT Data FROM ObjectBase WHERE Type='eventT' AND UID=?`, [UID], { cast: ['json'] });
        if (previous.length > 0) {
            oldData = previous[0].Data;
        }
    }

    const object = await renderObject(template, bodyData, { session }, 'events');
    object.Data = { ...bodyData, UID: undefined };

    if (!oldData) {
        await query(
            `
            INSERT INTO ObjectBase(UID,Type,UIDBelongsTo,Title,Display,SortName, FullTextIndex, stage,gender,Data)
            VALUES (?,'eventT',?,?,?,?,?,?,?,?)
           `,
            [
                UID,
                UUID2hex(session.root),
                object.Title,
                object.Display,
                object.SortIndex,
                object.FullTextIndex,
                object.stage,
                object.gender,
                JSON.stringify(object.Data),
            ]
        );

        publishEvent(`/add/root/eventT/${session.root}`, { organization: session.root, data: { type: 'add', objectType: 'eventT', UID: HEX2uuid(UID) } });
        // add the visibility filter
        await addVisibilityFilter(UID, object, { session });
    } else {
        const myDiff = {
            UID: HEX2uuid(UID),
            diffNew: diff(oldData, object.Data),
            diffOld: diff(object.Data, oldData),
            user: session.user,
        };
        await query(`UPDATE ObjectBase SET Title=?,Display=?,SortName=?,FullTextIndex=?,
            stage=?,gender=?,Data=? 
            WHERE UID=?`, [object.Title, object.Display, object.SortIndex, object.FullTextIndex, object.stage, object.gender, JSON.stringify(object.Data), UID]);

        // @ts-ignore
        if (myDiff.diffNew.templateVisibility || myDiff.diffNew.templateAdministration) {
            // rebuild visiblity
            // delete the old filters
            await deleteVisibilityFilter(UID);
            await addVisibilityFilter(UID, object, { session });
        }
        publishEvent(`/change/root/eventT/${session.root}`, { organization: session.root, data: { type: 'change', objectType: 'eventT', diff: myDiff } });
    }

    return { success: true, result: { ...object, UID: HEX2uuid(UID) } };
};

/**
 * Update an existing event template
 * @param {Buffer} UID - Template UID
 * @param {Object} bodyData - Request body data
 * @param {Object} session - User session
 * @returns {Promise<Object>} Result with success status and diff data
 */
export const updateEventTemplate = async (UID, bodyData, session) => {
    const previous = await query(`SELECT Data FROM ObjectBase WHERE Type='eventT' AND UID=?`, [UID], { cast: ['json'] });
    if (previous.length === 0) {
        throw new Error('event template not found');
    }
    const oldData = previous[0].Data;
    const template = Templates[session.root].eventTemplate;

    const object = await renderObject(template, { ...oldData, ...bodyData }, { session }, 'events');
    object.Data = { ...oldData, ...bodyData };
    delete object.Data.UID;

    const myDiff = {
        UID: HEX2uuid(UID),
        diffNew: diff(oldData, object.Data),
        diffOld: diff(object.Data, oldData),
        user: session.user,
    };

    await query(`UPDATE ObjectBase SET Title=?,Display=?,SortName=?,FullTextIndex=?,
        stage=?,gender=?,Data=? 
        WHERE UID=?`, [object.Title, object.Display, object.SortIndex, object.FullTextIndex, object.stage, object.gender, JSON.stringify(object.Data), UID]);

    // @ts-ignore
    if (myDiff.diffNew.templateVisibility || myDiff.diffNew.templateAdministration) {
        // rebuild visiblity
        // delete the old filters
        await deleteVisibilityFilter(UID);
        await addVisibilityFilter(UID, object, { session });
    }
    publishEvent(`/change/root/eventT/${session.root}`, { organization: session.root, data: { type: 'change', objectType: 'eventT', diff: myDiff } });

    return { success: true, result: myDiff };
};

/**
 * Delete an event template
 * @param {Buffer} UID - Template UID
 * @param {string} root - Organization root UID
 * @returns {Promise<Object>} Result with success status
 */
export const deleteEventTemplate = async (UID, root) => {
    await query(`DELETE FROM ObjectBase WHERE UID=? AND Type='eventT'
                 AND UIDBelongsTo=?`, [UID, UUID2hex(root)]);
    await deleteVisibilityFilter(UID);
    publishEvent(`/remove/root/eventT/${root}`, { organization: root, data: { type: 'remove', objectType: 'eventT', UID: HEX2uuid(UID) } });

    return { success: true };
};

/**
 * Get all templates for the organization
 * @param {string} root - Organization root UID
 * @param {string} user - User UID
 * @param {boolean} isAdminUser - Whether user is admin
 * @returns {Promise<Array>} List of templates
 */
export const getAllEventTemplates = async (root, user, isAdminUser) => {
    let visibleSql = '';
    if (!isAdminUser) visibleSql = `INNER JOIN Visible ON (Visible.UID=Main.UID AND Visible.UIDUser=U_UUID2BIN('${user}'))`;

    const result = await query(
        `
        SELECT
            Main.UID,  Main.Display , Main.SortName
        FROM
            ObjectBase  AS Main
        ${visibleSql}
        WHERE
           Main.UIDBelongsTo=? AND Main.Type='eventT'
        GROUP BY
        Main.UID
        ORDER BY
            Main.dindex      
        `,
        [UUID2hex(root)],
        { cast: ['UUID', 'json'] }
    );
    return result;
};

/**
 * Get all templates with data for the organization
 * @param {string} root - Organization root UID
 * @param {string} user - User UID
 * @param {boolean} isAdminUser - Whether user is admin
 * @returns {Promise<Array>} List of templates with data
 */
export const getAllEventTemplatesWithData = async (root, user, isAdminUser) => {
    let visibleSql = '';
    if (!isAdminUser) visibleSql = `INNER JOIN Visible ON (Visible.UID=Main.UID AND Visible.UIDUser=U_UUID2BIN('${user}'))`;

    const result = await query(
        `
        SELECT
            Main.UID,  Main.Display , Main.SortName, Main.Data
        FROM
            ObjectBase  AS Main
        ${visibleSql}
        WHERE
           Main.UIDBelongsTo=? AND Main.Type='eventT'
        GROUP BY
        Main.UID
        ORDER BY
            Main.dindex     
        `,
        [UUID2hex(root)],
        {
            log: false,
            cast: ['UUID', 'json'],
        }
    );
    return result;
};

/**
 * Get all templates the user can administer
 * @param {string} root - Organization root UID
 * @param {string} user - User UID
 * @param {boolean} isAdminUser - Whether user is admin
 * @returns {Promise<Array>} List of administrable templates
 */
export const getAdminEventTemplates = async (root, user, isAdminUser) => {
    let visibleSql = '';
    if (!isAdminUser)
        visibleSql = `INNER JOIN Visible ON (Visible.UID=Main.UID AND Visible.UIDUser=U_UUID2BIN('${user}') AND Visible.Type='changeable')`;

    const result = await query(
        `
        SELECT
            Main.UID,  Main.Display , Main.SortName, Main.Data
        FROM
            ObjectBase  AS Main
        ${visibleSql}
        WHERE
           Main.UIDBelongsTo=? AND Main.Type='eventT'
        GROUP BY
        Main.UID
        ORDER BY
            Main.dindex      
        `,
        [UUID2hex(root)],
        { cast: ['UUID', 'json'] }
    );
    return result;
};

/**
 * Get a specific event template by UID
 * @param {Buffer} UID - Template UID
 * @param {string} root - Organization root UID
 * @returns {Promise<Object>} Template data
 */
export const getEventTemplateByUID = async (UID, root) => {
    const result = await query(
        `
        SELECT
            Main.UID,  Main.Display , Main.SortName, Main.Data
        FROM
            ObjectBase  AS Main

        WHERE
           Main.UIDBelongsTo=? AND Main.Type='eventT' AND Main.UID=?
        GROUP BY
        Main.UID
        ORDER BY
            Main.dindex      
        `,
        [UUID2hex(root), UID],
        { cast: ['UUID', 'json'] }
    );
    return result[0];
};

/**
 * Get the template for an event with that UID
 * @param {Buffer} UID - Event UID
 * @param {string} root - Organization root UID
 * @returns {Promise<Object>} Template data
 */
export const getEventTemplateByEventUID = async (UID, root) => {
    const result = await query(
        `
        SELECT
            Main.UID,  Main.Display , Main.SortName, Main.Data
        FROM
            ObjectBase  AS Main
        INNER JOIN Links ON (Links.UIDTarget=Main.UID AND Links.Type='event' )

        WHERE
           Main.UIDBelongsTo=? AND Main.Type='eventT' AND Links.UID=?
        GROUP BY
        Main.UID
         
        `,
        [UUID2hex(root), UID],
        { cast: ['UUID', 'json'] }
    );
    return result[0];
};