var SEVERITY_COLORS = [
    '#97AAB3', '#7499FF', '#FFC859',
    '#FFA059', '#E97659', '#E45959'
];

var RESOLVE_COLOR = '#009900';

var SEND_MODE_HANDLERS = {
    alarm: handlerAlarm,
    event: handlerEvent
};

if (!String.prototype.format) {
    String.prototype.format = function() {
        var args = arguments;

        return this.replace(/{(\d+)}/g, function(match, number) {
            return number in args
                ? args[number]
                : match
            ;
        });
    };
}

function isEventProblem(params) {
    return params.event_value == 1
        && params.event_update_status == 0
    ;
}

function isEventUpdate(params) {
    return params.event_value == 1
        && params.event_update_status == 1
    ;
}

function isEventResolve(params) {
    return params.event_value == 0;
}

function getPermalink(mattermost_url, team_name, postid) {
    return '{0}/{1}/pl/{2}'.format(
        mattermost_url.replace(/\/+$/, ''),
        team_name,
        postid
    );
}

function getChannel(send_to) {
    switch (true) {
        case /.+\/#.+/.test(send_to):
            return getChannelByName(send_to);

        case /@.+/.test(send_to):
            return getDirectChannel(send_to);

        default:
            return getChannelByID(send_to);
    }
}

function getChannelByName(send_to) {
    var team_chan = send_to
        .trim()
        .split('/#');

    var resp = JSON.parse(req.Get(
        Mattermost.channel_byname.format(team_chan[0], team_chan[1]),
        JSON.stringify(fields)
    )
    );

    if (req.Status() != 200) {
        throw '[{0}] {1}'.format(resp.status_code, resp.message);
    }

    return resp;
}

function getDirectChannel(send_to) {
    Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
        arguments.callee.name,
        JSON.stringify(arguments)
    ));

    var teamUser = send_to
            .trim()
            .split('/@'),
        bot = getBotUser(),
        user = getUserByName(teamUser[1]);

    var resp = JSON.parse(req.Post(
        Mattermost.direct_channel,
        JSON.stringify([bot.id, user.id])
    )
    );

    Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
        arguments.callee.name,
        JSON.stringify(resp)
    ));

    if (req.Status() != 201) {
        throw '[{0}] {1}'.format(resp.status_code, resp.message);
    }

    resp.team_name = teamUser[0];

    return resp;
}

function getChannelByID(channelID) {
    Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
        arguments.callee.name,
        JSON.stringify(arguments)
    ));

    var resp = JSON.parse(req.Get(
        Mattermost.get_channel.format(channelID),
        JSON.stringify(fields)
    )
    );

    Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
        arguments.callee.name,
        JSON.stringify(resp)
    ));

    if (req.Status() != 200) {
        throw '[{0}] {1}'.format(resp.status_code, resp.message);
    }

    return resp;
}

function getBotUser() {
    Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
        arguments.callee.name,
        JSON.stringify(arguments)
    ));

    var resp = JSON.parse(req.Get(
        Mattermost.bot_user,
        JSON.stringify(fields)
    )
    );

    Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
        arguments.callee.name,
        JSON.stringify(resp)
    ));

    if (req.Status() != 200) {
        throw '[{0}] {1}'.format(resp.status_code, resp.message);
    }

    return resp;
}

function getUserByName(userName) {
    Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
        arguments.callee.name,
        JSON.stringify(arguments)
    ));

    var resp = JSON.parse(req.Get(
        Mattermost.user_byname.format(userName),
        JSON.stringify(fields)
    )
    );

    Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
        arguments.callee.name,
        JSON.stringify(resp)
    ));

    if (req.Status() != 200) {
        throw '[{0}] {1}'.format(resp.status_code, resp.message);
    }

    return resp;
}

function getTeamByID(teamID) {
    Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
        arguments.callee.name,
        JSON.stringify(arguments)
    ));

    var resp = JSON.parse(req.Get(
        Mattermost.get_team.format(teamID),
        JSON.stringify(fields)
    )
    );

    Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
        arguments.callee.name,
        JSON.stringify(resp)
    ));

    if (req.Status() != 200) {
        throw '[{0}] {1}'.format(resp.status_code, resp.message);
    }

    return resp;
}

function createProblemURL(zabbix_url, triggerid, eventid, event_source) {
    var problem_url = '';
    if (event_source === '0') {
        problem_url = '{0}/tr_events.php?triggerid={1}&eventid={2}'
            .format(
                zabbix_url,
                triggerid,
                eventid
            );
    }
    else {
        problem_url = zabbix_url;
    }

    return problem_url;
}

function getTagValue(event_tags, key) {
    var pattern = new RegExp('(' + key + ':.+)');
    var tagValue = event_tags
        .split(',')
        .filter(function (v) {
            return v.match(pattern);
        })
        .map(function (v) {
            return v.split(':')[1];
        })[0]
        || 0;

    return tagValue;
}

function handlerAlarm(req, params) {
    var channel = getChannel(params.send_to);
    var fields = {
        channel_id: channel.id,
        props: {}
    };

    if (isEventProblem(params)) {
        var team_name = channel.team_name
            ? channel.team_name
            : getTeamByID(channel.team_id).name;

        fields.props.attachments = [
            createMessage(
                SEVERITY_COLORS[params.event_nseverity] || 0,
                params.event_date,
                params.event_time,
                createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source)
            )
        ];

        var resp = JSON.parse(req.Post(
            Mattermost.post_message,
            JSON.stringify(fields)
        )
        );

        if (req.Status() != 201) {
            throw '[{0}] {1}'.format(resp.status_code, resp.message);
        }

        result.tags.__mattermost_post_id = resp.id;
        result.tags.__mattermost_channel_id = channel.id;
        result.tags.__mattermost_channel_name = channel.name;
        result.tags.__mattermost_message_link = getPermalink(
            params.mattermost_url,
            team_name,
            resp.id
        );

    }
    else if (isEventUpdate(params)) {
        fields.root_id = getTagValue(params.event_tags, 'mattermost_post_id');

        if (params.event_source === '0') {}
        fields.props.attachments = [
            createMessage(
                SEVERITY_COLORS[params.event_nseverity] || 0,
                params.event_update_date,
                params.event_update_time,
                createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source),
                true
            )
        ];

        resp = JSON.parse(req.Post(
            Mattermost.post_message, JSON.stringify(fields)
        )
        );

        if (req.Status() != 201) {
            throw '[{0}] {1}'.format(resp.status_code, resp.message);
        }

    }
    else if (isEventResolve(params)) {
        fields.channel_id = getTagValue(params.event_tags, 'mattermost_channel_id');
        fields.id = getTagValue(params.event_tags, 'mattermost_post_id');
        fields.props.attachments = [
            createMessage(
                RESOLVE_COLOR,
                params.event_date,
                params.event_time,
                createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source)
            )
        ];

        var post_id = getTagValue(params.event_tags, 'mattermost_post_id');

        resp = JSON.parse(req.Put(
            Mattermost.chat_update.format(post_id),
            JSON.stringify(fields)
        )
        );

        if (req.Status() != 200) {
            throw '[{0}] {1}'.format(resp.status_code, resp.message);
        }
    }
}

function handlerEvent(req, params) {
    var channel = getChannel(params.send_to);
    var fields = {
        channel_id: channel.id,
        props: {}
    };

    if (isEventProblem(params)) {
        var team_name = channel.team_name
            ? channel.team_name
            : getTeamByID(channel.team_id).name;

        fields.props.attachments = [
            createMessage(
                SEVERITY_COLORS[params.event_nseverity] || 0,
                params.event_date,
                params.event_time,
                createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source)
            )
        ];

        var resp = JSON.parse(req.Post(Mattermost.post_message, JSON.stringify(fields)));

        if (req.Status() != 201) {
            throw '[{0}] {1}'.format(resp.status_code, resp.message);
        }

        result.tags.__mattermost_channel_name = channel.name;
        result.tags.__mattermost_message_link = getPermalink(
            params.mattermost_url,
            team_name,
            resp.id
        );

    }
    else if (isEventUpdate(params)) {
        fields.props.attachments = [
            createMessage(
                SEVERITY_COLORS[params.event_nseverity] || 0,
                params.event_update_date,
                params.event_update_time,
                createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source),
                false
            )
        ];

        resp = JSON.parse(req.Post(Mattermost.post_message, JSON.stringify(fields)));

        if (req.Status() != 201) {
            throw '[{0}] {1}'.format(resp.status_code, resp.message);
        }

    }
    else if (isEventResolve(params)) {
        fields.props.attachments = [
            createMessage(
                RESOLVE_COLOR,
                params.event_recovery_date,
                params.event_recovery_time,
                createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source)
            )
        ];

        resp = JSON.parse(req.Post(Mattermost.post_message, JSON.stringify(fields)));

        if (req.Status() != 201) {
            throw '[{0}] {1}'.format(resp.status_code, resp.message);
        }
    }
}

function createMessage(
    event_severity_color,
    event_date,
    event_time,
    problem_url,
    isShort
) {
    var message = {
        fallbac: params.alert_subject,
        title: params.alert_subject,
        color: event_severity_color,
        title_link: problem_url,
        footer: problem_url,

        fields: [
            {
                title: 'Host',
                value: '{0} [{1}]'.format(params.host_name, params.host_ip),
                short: true
            },
            {
                title: 'Event time',
                value: '{0} {1}'.format(event_date, event_time),
                short: true
            }
        ],
    };

    
    if (params.event_source === '0') {
        message.fields.push(
            {
                title: 'Severity',
                value: params.event_severity,
                short: true
            },
            {
                title: 'Opdata',
                value: params.event_opdata,
                short: true
            }
        );
    }

    if (!isShort && params.event_source === '0') {
        message.fields.push(
            {
                title: 'Event tags',
                value: '`{0}`'.format(params.event_tags.replace(/__.+?:(.+?,|.+)/g, '') || 'None'),
                short: true
            },
            {
                title: 'Trigger description',
                value: params.trigger_description,
                short: true
            }
        );
    }

    if (params.event_source !== '0' || params.event_update_status === '1') {
        message.fields.push(
            {
                title: 'Details',
                value: params.alert_message,
                short: false
            }
        );
    }

    return message;
}

function validateParams(params) {
    if (typeof params.bot_token !== 'string' || params.bot_token.trim() === '') {
        throw 'Field "bot_token" cannot be empty';
    }

    if (isNaN(params.event_id)) {
        throw 'Field "event_id" is not a number';
    }

    if ([0, 1, 2, 3].indexOf(parseInt(params.event_source)) === -1) {
        throw 'Incorrect "event_source" parameter given: "' + params.event_source + '".\nMust be 0-3.';
    }

    if (params.event_source !== '0') {
        params.event_nseverity = '0';
        params.event_severity = 'Not classified';
        params.event_update_status = '0';
        params.send_mode = 'event';
    }

    if (params.event_source === '1' || params.event_source === '2') {
        params.event_value = '1';
    }

    if (params.event_source === '1') {
        params.host_name = params.discovery_host_dns;
        params.host_ip = params.discovery_host_ip;
    }

    if ([0, 1, 2, 3, 4, 5].indexOf(parseInt(params.event_nseverity)) === -1) {
        throw 'Incorrect "event_nseverity" parameter given: ' + params.event_nseverity + '\nMust be 0-5.';
    }

    if (typeof params.event_severity !== 'string' || params.event_severity.trim() === '') {
        throw 'Field "event_severity" cannot be empty';
    }

    if (params.event_update_status !== '0' && params.event_update_status !== '1') {
        throw 'Incorrect "event_update_status" parameter given: ' + params.event_update_status + '\nMust be 0 or 1.';
    }

    if (params.event_value !== '0' && params.event_value !== '1') {
        throw 'Incorrect "event_value" parameter given: ' + params.event_value + '\nMust be 0 or 1.';
    }

    if (typeof params.host_ip !== 'string' || params.host_ip.trim() === '') {
        throw 'Field "host_ip" cannot be empty';
    }

    if (typeof params.host_name !== 'string' || params.host_name.trim() === '') {
        throw 'Field "host_name" cannot be empty';
    }

    if (typeof params.mattermost_url !== 'string' || params.mattermost_url.trim() === '') {
        throw 'Field "mattermost_url" cannot be empty';
    }

    if (!/^(http|https):\/\/.+/.test(params.mattermost_url)) {
        throw 'Field "mattermost_url" must contain a schema';
    }

    if (['alarm', 'event'].indexOf(params.send_mode) === -1) {
        throw 'Incorrect "send_mode" parameter given: ' + params.send_mode + '\nMust be "alarm" or "event".';
    }

    if (typeof params.send_to !== 'string' || params.send_to.trim() === '') {
        throw 'Field "send_to" cannot be empty';
    }

    if (isNaN(params.trigger_id) && params.event_source === '0') {
        throw 'field "trigger_id" is not a number';
    }

    if (typeof params.zabbix_url !== 'string' || params.zabbix_url.trim() === '') {
        throw 'Field "zabbix_url" cannot be empty';
    }

    if (!/^(http|https):\/\/.+/.test(params.zabbix_url)) {
        throw 'Field "zabbix_url" must contain a schema';
    }

}

try {
    var params = JSON.parse(value);

    validateParams(params);

    var req = new CurlHttpRequest(),
        fields = {},
        result = {tags: {}};

    if (typeof params.HTTPProxy === 'string' && params.HTTPProxy.trim() !== '') {
        req.SetProxy(params.HTTPProxy);
    }

    req.AddHeader('Content-Type: application/json; charset=utf-8');
    req.AddHeader('Authorization: Bearer ' + params.bot_token);

    params.mattermost_url = params.mattermost_url.replace(/\/+$/, '');
    params.zabbix_url = params.zabbix_url.replace(/\/+$/, '');

    var APIEndpoint = params.mattermost_url + '/api/v4/';

    var Mattermost = {
        post_message: APIEndpoint + 'posts',
        get_channel: APIEndpoint + 'channels/{0}',
        get_team: APIEndpoint + 'teams/{0}',
        chat_update: APIEndpoint + 'posts/{0}',
        direct_channel: APIEndpoint + 'channels/direct',
        channel_byname: APIEndpoint + 'teams/name/{0}/channels/name/{1}',
        user_byname: APIEndpoint + 'users/username/{0}',
        bot_user: APIEndpoint + 'users/me'

    };

    params.send_mode = params.send_mode.toLowerCase();
    params.send_mode = params.send_mode in SEND_MODE_HANDLERS
        ? params.send_mode
        : 'alarm';

    SEND_MODE_HANDLERS[params.send_mode](req, params);

    if (params.event_source === '0') {
        return JSON.stringify(result);
    }
    else {
        return 'OK';
    }
}
catch (error) {
    Zabbix.Log(4, '[ Mattermost Webhook ] Mattermost notification failed: ' + error);
    throw 'Mattermost notification failed: ' + error;
}