// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.
define(['hp/core/LinkTargetBuilder', 'hp/services/Log'], 
function(linkTargetBuilder, log) {"use strict";

    // This is the starting point for formatting notification data from an
    // index result item or alert or task resource.

    var NotificationFormatter = ( function() {

        var INLINE_JSON_LINK_REGEXP = new RegExp('{\\"[^\\\\]+?}', 'g');
        var INLINE_MARKDOWN_LINK_REGEXP = new RegExp('\\[(.+?)\\]\\((/.+?)\\)', 'g');

        // ### new NotificationFormatter()
        // Create a new NotificationFormatter.
        function NotificationFormatter() {
            var categories = {}; // category : {associatedResourceCategory : [{func: func, pattern: pattern}, ...]}
            
            function findFormatterSet(formatters, notification, item, canFormat) {
                var set = [];
                var formatter, length = formatters.length;
                for (var i=0; i<length; i++) {
                    formatter = formatters[i];
                    if (canFormat(formatter, notification, item)) {
                        set.push(formatter);
                    }
                }
                return set;
            }
            
            function findFormatters(formatters, notification, item) {
                var formatterSet;
                var length;
                
                // Look for index formatters
                formatterSet = findFormatterSet(formatters, notification, item,
                    function (formatter, notification, item) {
                        return ('IndexResource' === item.type && formatter.index);
                    });
                
                // Look for formatters that match pattern
                if (formatterSet.length === 0) {
                    formatterSet = findFormatterSet(formatters, notification, item,
                        function (formatter, notification, item) {
                            return (formatter.pattern &&
                                    notification.summary.match(formatter.pattern));
                        }); 
                }
                
                // Look for formatters that match the namespace
                if (formatterSet.length === 0) {
                    formatterSet = findFormatterSet(formatters, notification, item,
                        function (formatter, notification, item) {
                            return ((formatter.namespace) && (notification.namespace) &&
                                    (formatter.namespace === notification.namespace));
                        });
                }
                
                // Look for formatters that register without namespace and pattern
                if (formatterSet.length === 0) {
                    formatterSet = findFormatterSet(formatters, notification, item,
                        function (formatter, notification, item) {
                            return (!(formatter.namespace) && !(formatter.pattern));
                        });
                }
                return formatterSet;  
            }
            
            function formatResourceCategories(categoryFormatters, notification, item) {
                var formatters = categoryFormatters[notification.sourceCategory];
                var formatterSet;
                var formatted = false;
                var length = 0;
                
                if (formatters) {
                    formatterSet = findFormatters(formatters, notification, item);
                } else {
                    formatterSet = categoryFormatters.any;
                }
                
                if (formatterSet) {
                    length = formatterSet.length;
                    if (length > 0) {
                        for (var i=0; i<length; i++) {
                            formatterSet[i].format(notification, item);
                        }
                        formatted = true;
                    }
                }

                return (formatted);
            }

            // ### register(formatterConfig)
            //
            // Registers a formatter specified by the configuration object:
            //
            // - **match** an object describing what to match:
            //
            //     - **category** 'alerts' or 'tasks'
            //     - **associatedResourceCategory** the category of the the associated resource.
            //         Specifying 'any' indicates that this formatter works for any resource
            //         category.
            //     - **pattern** an optional regular expression to match a particular activity name.
            //     - **namespace** an optional value to match a more specific set of notifications.
            //         For example, a formatter with a namespace property "server.delete" will be
            //         able to format a notification with the same namespace property under the
            //         same category+associatedResourceCategory.
            //
            // - **func** function called to format the notification from the item.
            //     Called as:
            //         function func(notification, item) {
            //             // update 'notification' from 'item'
            //         }
            this.register = function(formatterConfig) {
                if (! categories.hasOwnProperty(formatterConfig.category)) {
                    categories[formatterConfig.category] = {};
                }
                var categoryFormatters = categories[formatterConfig.category];
                if (! categoryFormatters.hasOwnProperty(
                    formatterConfig.associatedResourceCategory)) {
                    categoryFormatters[formatterConfig.associatedResourceCategory] = [];
                }
                var associatedFormatters =
                    categoryFormatters[formatterConfig.associatedResourceCategory];
                associatedFormatters.push(formatterConfig);
            };
            
            // ### format(notification, item)
            //
            // Called by a *NotificationPresenter to format the notification from the item.
            //
            // - **notification** the notification object to update
            // - **item** the index resource item or task or alert resource
            //
            // There could be more than 1 formatter that will be called to format this notification.
            // The rules of identifying these formatters are:
            // 
            // 1. The order of which formatter gets to format the message first is based on the order 
            //    of registration.
            // 2. Formatters registered with index:true are selected first if the item is of type "IndexResource"
            // 3. If the above selection is empty, then select formatters registered with a 'pattern' which 
            //    match the notificaitons.summary.
            // 4. If the above selection is empty, then select formatters that match the message’s “associatedResourceCategory” 
            //    and have a namespace. For example, the namespace values can be “appliance.settings”, “appliance.license.add”, 
            //    “server-hardware.delete”.
            // 5. If the above selection is empty, then select formatters that match the message’s “associatedResourceCategory” 
            //    without the namespace property. 
            // 6. If the above selection is empty, then select the 'any' formatter
            this.format = function(notification, item) {
                // only bother for notifications that look like Activity
                if (notification.hasOwnProperty('category') &&
                    notification.hasOwnProperty('sourceCategory')) {
                    var categoryFormatters = categories[notification.category];
                    if (categoryFormatters) {
                        formatResourceCategories(categoryFormatters, notification, item);
                    }
                }
            };

            // ### addDetail(notification, detail)
            // Adds a detail message to the notification
            // if it has not been added.
            this.addDetail = function (notification, detail) {
                if (! notification.details) {
                    notification.details = detail + '\n';
                } else if (notification.details.indexOf(detail) < 0) {
                    notification.details += detail + '\n';
                }
            };

            // ### addResolution(notification, resolution)
            // Adds a resolution message to the notification
            // if it has not been added.
            this.addResolution = function (notification, resolution) {
                if (! notification.resolution) {
                    notification.resolution = resolution;
                } else if (notification.resolution.indexOf(resolution) < 0) {
                    if (notification.resolution.slice(0, 2) !== '* ') {
                        // was a single item, convert to a list
                        notification.resolution = '* ' + notification.resolution + '\n';
                    }
                    notification.resolution += '* ' + resolution + '\n';
                }
            };

            // ### addAction(notification, action)
            // Adds an action to the notification if it has not been added. 
            // The action can either be a Markdown hyperlink (preferred) or
            // an HTML hyperlink.
            this.addAction = function (notification, action) {
                var length;
                var found = false;
                if (! notification.actions) {
                    notification.actions = [];
                } else {
                   length = notification.actions.length;
                   while (--length >= 0) {
                      if (notification.actions[length].indexOf(action) === 0) {
                          found = true;
                          break;
                      }
                   }
                }
                if (!found) {
                    notification.actions.push(action);
                }
            };

            // ### formatLink(object)
            //
            // Returns a Markdown syntax link for the object. If the object is a string,
            // it is first parsed as a JSON object. Object properties:
            //
            // - **name** the name of the resource or action
            // - **uri** the uri of the resource or fully specified browser URL
            // - **view** an optional view to link to, such as 'edit'
            this.formatLink = function(object) {
                if (typeof object === 'string') {
                    object = JSON.parse(object);
                }
                if (object.uri.lastIndexOf('http', 0) === 0) {
                    return '[' + object.name + '](' + object.uri + ')';
                } else {
                    var location = linkTargetBuilder.makeLocation(object.uri, object.view);
                    if (location) {
                        return '[' + object.name + '](#' + location + ')';
                    } else {
                        return object.name;
                    }
                }
            };
            
            // ### replaceInlineLinks(text)
            //
            // Replaces inline JSON link references with Markdown style links.
            // For instance, passing:
            //
            //     "Restore {'name':'server 1','uri':'/rest/servers/1'}"
            //
            // will return:
            //
            //     "Restore [server 1](/servers/show/r/rest/servers/1)"
            //
            this.replaceInlineLinks = function (text) {
                var result = '';
                if (text) {
                    result = text.replace(INLINE_MARKDOWN_LINK_REGEXP, function (m, name, uri) {
                        return '[' + name + '](#' +
                            linkTargetBuilder.makeLocation(uri) + ')';
                    });
                    result = result.replace(INLINE_JSON_LINK_REGEXP, function (m) {
                        var obj = JSON.parse(m);
                        if (obj.hasOwnProperty('name') && obj.hasOwnProperty('uri')) {
                            if (obj.uri.lastIndexOf('http', 0) === 0) {
                                return '[' + obj.name + '](' + obj.uri + ')';
                            } else {
                                return '[' + obj.name + '](#' +
                                    linkTargetBuilder.makeLocation(obj.uri) + ')';
                            }
                        } else {
                            return m;
                        }
                    });
                }
                return result;
            };

            // ### colapseInlineLinks(text)
            // Reduces the links in the specified text to just their names.
            this.collapseInlineLinks = function (text) {
                var result = '';
                if (text) {
                    result = text.replace(INLINE_MARKDOWN_LINK_REGEXP, function (m, name, uri) {
                        return name;
                    });
                    result = result.replace(INLINE_JSON_LINK_REGEXP, function (m) {
                        var obj = JSON.parse(m);
                        if (obj.hasOwnProperty('name') && obj.hasOwnProperty('uri')) {
                            return obj.name;
                        } else {
                            return m;
                        }
                    });
                }
                return result;
            };
        }

        return new NotificationFormatter();
    }());

    return NotificationFormatter;
});
