// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { Component } from '@angular/core';
import { IonicPage, NavParams } from 'ionic-angular';
import { Subscription } from 'rxjs';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreEventsProvider, CoreEventObserver } from '@providers/events';
import { CoreSitesProvider } from '@providers/sites';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { AddonNotificationsProvider } from '../../providers/notifications';
import { AddonPushNotificationsDelegate } from '@addon/pushnotifications/providers/delegate';

/**
 * Page that displays the list of notifications.
 */
@IonicPage({ segment: 'addon-notifications-list' })
@Component({
    selector: 'page-addon-notifications-list',
    templateUrl: 'list.html',
})
export class AddonNotificationsListPage {

    notifications = [];
    notificationsLoaded = false;
    canLoadMore = false;
    loadMoreError = false;
    canMarkAllNotificationsAsRead = false;
    loadingMarkAllNotificationsAsRead = false;

    protected readCount = 0;
    protected unreadCount = 0;
    protected cronObserver: CoreEventObserver;
    protected pushObserver: Subscription;

    constructor(navParams: NavParams, private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
            private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider,
            private utils: CoreUtilsProvider, private notificationsProvider: AddonNotificationsProvider,
            private pushNotificationsDelegate: AddonPushNotificationsDelegate) {
    }

    /**
     * View loaded.
     */
    ionViewDidLoad(): void {
        this.fetchNotifications().finally(() => {
            this.notificationsLoaded = true;
        });

        this.cronObserver = this.eventsProvider.on(AddonNotificationsProvider.READ_CRON_EVENT, () => this.refreshNotifications(),
                this.sitesProvider.getCurrentSiteId());

        this.pushObserver = this.pushNotificationsDelegate.on('receive').subscribe((notification) => {
            // New notification received. If it's from current site, refresh the data.
            if (this.utils.isTrueOrOne(notification.notif) && this.sitesProvider.isCurrentSite(notification.site)) {
                this.refreshNotifications();
            }
        });
    }

    /**
     * Convenience function to get notifications. Gets unread notifications first.
     *
     * @param {boolean} refreh Whether we're refreshing data.
     * @return {Promise<any>} Resolved when done.
     */
    protected fetchNotifications(refresh?: boolean): Promise<any> {
        this.loadMoreError = false;

        if (refresh) {
            this.readCount = 0;
            this.unreadCount = 0;
        }

        const limit = AddonNotificationsProvider.LIST_LIMIT;

        return this.notificationsProvider.getUnreadNotifications(this.unreadCount, limit).then((unread) => {
            const promises = [];

            unread.forEach(this.formatText.bind(this));

            /* Don't add the unread notifications to this.notifications yet. If there are no unread notifications
               that causes that the "There are no notifications" message is shown in pull to refresh. */
            this.unreadCount += unread.length;

            if (unread.length < limit) {
                // Limit not reached. Get read notifications until reach the limit.
                const readLimit = limit - unread.length;
                promises.push(this.notificationsProvider.getReadNotifications(this.readCount, readLimit).then((read) => {
                    read.forEach(this.formatText.bind(this));
                    this.readCount += read.length;
                    if (refresh) {
                        this.notifications = unread.concat(read);
                    } else {
                        this.notifications = this.notifications.concat(unread, read);
                    }
                    this.canLoadMore = read.length >= readLimit;
                }).catch((error) => {
                    if (unread.length == 0) {
                        this.domUtils.showErrorModalDefault(error, 'addon.notifications.errorgetnotifications', true);
                        this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
                    }
                }));
            } else {
                if (refresh) {
                    this.notifications = unread;
                } else {
                    this.notifications = this.notifications.concat(unread);
                }
                this.canLoadMore = true;
            }

            return Promise.all(promises).then(() => {
                // Mark retrieved notifications as read if they are not.
                this.markNotificationsAsRead(unread);
            });
        }).catch((error) => {
            this.domUtils.showErrorModalDefault(error, 'addon.notifications.errorgetnotifications', true);
            this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
        });
    }

    /**
     * Mark all notifications as read.
     */
    markAllNotificationsAsRead(): void {
        this.loadingMarkAllNotificationsAsRead = true;
        this.notificationsProvider.markAllNotificationsAsRead().catch(() => {
            // Omit failure.
        }).then(() => {
            const siteId = this.sitesProvider.getCurrentSiteId();
            this.eventsProvider.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, null, siteId);

            // All marked as read, refresh the list.
            this.notificationsLoaded = false;

            return this.refreshNotifications().finally(() => {
                this.notificationsLoaded = true;
            });
        });
    }

    /**
     * Mark notifications as read.
     *
     * @param {any[]} notifications Array of notification objects.
     */
    protected markNotificationsAsRead(notifications: any[]): void {
        let promise;

        if (notifications.length > 0) {
            const promises = notifications.map((notification) => {
                return this.notificationsProvider.markNotificationRead(notification.id);
            });

            promise = Promise.all(promises).catch(() => {
                // Ignore errors.
            }).finally(() => {
                this.notificationsProvider.invalidateNotificationsList().finally(() => {
                    const siteId = this.sitesProvider.getCurrentSiteId();
                    this.eventsProvider.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, null, siteId);
                });
            });
        } else {
            promise = Promise.resolve();
        }

        promise.finally(() => {
            // Check if mark all notifications as read is enabled and there are some to read.
            if (this.notificationsProvider.isMarkAllNotificationsAsReadEnabled()) {
                this.loadingMarkAllNotificationsAsRead = true;

                return this.notificationsProvider.getUnreadNotificationsCount().then((unread) => {
                    this.canMarkAllNotificationsAsRead = unread > 0;
                }).finally(() => {
                    this.loadingMarkAllNotificationsAsRead = false;
                });
            }
            this.canMarkAllNotificationsAsRead = false;
        });
    }

    /**
     * Refresh notifications.
     *
     * @param {any} [refresher] Refresher.
     * @return Promise<any> Promise resolved when done.
     */
    refreshNotifications(refresher?: any): Promise<any> {
        return this.notificationsProvider.invalidateNotificationsList().finally(() => {
            return this.fetchNotifications(true).finally(() => {
                if (refresher) {
                    refresher.complete();
                }
            });
        });
    }

    /**
     * Load more results.
     *
     * @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
     */
    loadMoreNotifications(infiniteComplete?: any): void {
        this.fetchNotifications().finally(() => {
            infiniteComplete && infiniteComplete();
        });
    }

    /**
     * Formats the text of a notification.
     *
     * @param {any} notification The notification object.
     */
    protected formatText(notification: any): void {
        const text = notification.mobiletext.replace(/-{4,}/ig, '');
        notification.mobiletext = this.textUtils.replaceNewLines(text, '<br>');
    }

    /**
     * Page destroyed.
     */
    ngOnDestroy(): void {
        this.cronObserver && this.cronObserver.off();
        this.pushObserver && this.pushObserver.unsubscribe();
    }
}
