"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.NotificationManager = exports.buildKey = void 0;
const server_api_1 = require("@signalk/server-api");
const alarm_1 = require("./alarm");
const uuid = __importStar(require("uuid"));
const _ = __importStar(require("lodash"));
const CLEAN_INTERVAL = 60000;
/**
 *
 * @param context Signal K Context
 * @param path Signal K Path
 * @param source Delta sourceRef
 * @returns String representing a key associating notification deltas to their notificationId
 */
const buildKey = (context, path, source) => {
    return `${context}/${path}/${source}`;
};
exports.buildKey = buildKey;
/**
 * Class to manage the lifecycle of alarms
 */
class NotificationManager {
    server;
    app;
    alarms = new Map();
    deltaVersion = server_api_1.SKVersion.v1;
    cleanTimer;
    forCleaning = [];
    constructor(server) {
        this.server = server;
        this.app = server;
        // start cleanup timer
        this.cleanTimer = setInterval(() => this.clean(), CLEAN_INTERVAL);
    }
    /**
     * Emit notification for the supplied alarm object
     * @param alarm Alarm object
     */
    emitNotification(alarm) {
        this.app.handleMessage('notificationApi', alarm?.delta, this.deltaVersion);
    }
    /** Return a list of Alarms keyed by their id */
    get list() {
        const l = {};
        this.alarms.forEach((v, k) => {
            l[k] = v.properties;
        });
        return l;
    }
    /**
     * Return alarm with specified identifier
     * @param id alarm identifier
     * @returns alarm properties
     */
    get(id) {
        return this.alarms.get(id)?.properties;
    }
    /**
     * Raise alarm and return identifier
     * @param options Object to initialise the Alarm
     * @returns alarm id
     */
    raise(options) {
        const id = uuid.v4();
        const alarm = new alarm_1.Alarm(id);
        alarm.value.state = options.state;
        alarm.status.canSilence =
            options.state === server_api_1.ALARM_STATE.emergency ? false : true;
        if (options.path) {
            alarm.setPath(options.path, options.appendId ? id : undefined);
        }
        if (options.message) {
            alarm.value.message = options.message;
        }
        if (options.position || options.state === server_api_1.ALARM_STATE.emergency) {
            alarm.value.position =
                /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
                _.get(this.app.signalk.self, 'navigation.position')?.value ??
                    null;
        }
        if (options.createdAt || options.state === server_api_1.ALARM_STATE.emergency) {
            alarm.value.createdAt = new Date().toISOString();
        }
        /*if (options.meta) {
          (alarm.value as any).meta = options.meta
        }*/
        this.alarms.set(id, alarm);
        this.emitNotification(alarm);
        return id;
    }
    /**
     * Update alarm properties
     * @param id Alarm identifier
     * @param options Key / values to update
     */
    update(id, options) {
        const alarm = this.alarms.get(id);
        if (!alarm) {
            throw new Error('Notification not found!');
        }
        if (options.state) {
            alarm.value.state = options.state;
            alarm.status.canSilence =
                options.state === server_api_1.ALARM_STATE.emergency ? false : true;
        }
        if (options.message) {
            alarm.value.message = options.message;
        }
        /*if (options.meta) {
          (alarm.value as any).meta = options.meta
        }*/
        this.alarms.set(id, alarm);
        this.emitNotification(alarm);
    }
    /**
     * Raise MOB alarm and return identifier
     * @param options  Object to initialise the alarm. default= 'Person Overboard!'
     * @returns alarm id
     */
    mob(options) {
        return this.raise({
            state: server_api_1.ALARM_STATE.emergency,
            message: options?.message ?? 'Person Overboard!',
            path: 'mob',
            appendId: true,
            position: true,
            createdAt: true
        });
    }
    /**
     * Silence All alarms
     */
    silenceAll() {
        this.alarms.forEach((alarm) => {
            try {
                alarm?.silence();
                this.emitNotification(alarm);
            }
            catch {
                // already silenced
            }
        });
    }
    /**
     * Silence alarm by removing the 'sound' method from the notification
     * @param id Notification identifier
     */
    silence(id) {
        if (!this.alarms.has(id)) {
            throw new Error('Alarm not found!');
        }
        const alarm = this.alarms.get(id);
        alarm?.silence();
        this.emitNotification(alarm);
    }
    /**
     * Acknowledge All alarms
     */
    acknowledgeAll() {
        this.alarms.forEach((alarm) => {
            alarm?.acknowledge();
            this.emitNotification(alarm);
        });
    }
    /**
     * Acknowledge alarm by removing the 'sound' method from the notification
     * @param id Notification identifier
     */
    acknowledge(id) {
        if (!this.alarms.has(id)) {
            throw new Error('Alarm not found!');
        }
        const alarm = this.alarms.get(id);
        alarm?.acknowledge();
        this.emitNotification(alarm);
    }
    /**
     * Clear alarm by setting notification state to `normal`
     * @param id Notification identifier
     */
    clear(id) {
        if (!this.alarms.has(id)) {
            throw new Error('Alarm not found!');
        }
        const alarm = this.alarms.get(id);
        alarm?.clear();
        this.emitNotification(alarm);
    }
    /**
     * Process alarm from notification delta
     * @param u Update object of incoming Delta message
     * @param context Incoming Delta message context value
     */
    processNotificationUpdate(u, context) {
        if ((0, server_api_1.hasValues)(u) && u.values.length) {
            const id = u.notificationId;
            let alarm;
            if (this.alarms.has(id)) {
                alarm = this.alarms.get(id);
                alarm.syncFromNotificationUpdate(u, context);
                this.alarms.set(id, alarm);
            }
            else {
                alarm = new alarm_1.Alarm();
                alarm.syncFromNotificationUpdate(u, context);
                this.alarms.set(id, alarm);
            }
            this.emitNotification(alarm);
        }
    }
    /**
     * Clean out alarms that have returned to and remained in NORMAL state
     * for the duration of CLEAN_INTERVAL
     */
    clean() {
        const idsToDelete = [];
        const nextClean = [];
        this.alarms.forEach((v, k) => {
            if (this.forCleaning.includes(k) &&
                v.value?.state === server_api_1.ALARM_STATE.normal) {
                idsToDelete.push(k);
            }
            else {
                nextClean.push(k);
            }
        });
        this.forCleaning = nextClean;
        idsToDelete.forEach((id) => {
            this.alarms.delete(id);
        });
    }
}
exports.NotificationManager = NotificationManager;
//# sourceMappingURL=notificationManager.js.map