// (C) Copyright 2015 Moodle Pty Ltd.
//
// 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, OnDestroy, OnInit } from '@angular/core';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
import {
    AddonMessages,
    AddonMessagesDiscussion,
    AddonMessagesMessageAreaContact,
    AddonMessagesProvider,
} from '../../services/messages';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils';
import { CoreApp } from '@services/app';
import { ActivatedRoute, Params } from '@angular/router';
import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications';
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
import { Subscription } from 'rxjs';
import { Translate, Platform } from '@singletons';
import { IonRefresher } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen';

/**
 * Page that displays the list of discussions.
 */
@Component({
    selector: 'addon-messages-discussions',
    templateUrl: 'discussions.html',
    styleUrls: ['../../messages-common.scss'],
})
export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {

    protected newMessagesObserver: CoreEventObserver;
    protected readChangedObserver: CoreEventObserver;
    protected appResumeSubscription: Subscription;
    protected pushObserver: Subscription;
    protected loadingMessages: string;
    protected siteId: string;

    loaded = false;
    loadingMessage = '';
    discussions: AddonMessagesDiscussion[] = [];
    discussionUserId?: number;

    search = {
        enabled: false,
        showResults: false,
        results: <AddonMessagesMessageAreaContact[]> [],
        loading: '',
        text: '',
    };

    constructor(
        protected route: ActivatedRoute,
    ) {

        this.search.loading =  Translate.instant('core.searching');
        this.loadingMessages = Translate.instant('core.loading');
        this.siteId = CoreSites.getCurrentSiteId();

        // Update discussions when new message is received.
        this.newMessagesObserver = CoreEvents.on(
            AddonMessagesProvider.NEW_MESSAGE_EVENT,
            (data) => {
                if (data.userId && this.discussions) {
                    const discussion = this.discussions.find((disc) => disc.message!.user == data.userId);

                    if (typeof discussion == 'undefined') {
                        this.loaded = false;
                        this.refreshData().finally(() => {
                            this.loaded = true;
                        });
                    } else {
                    // An existing discussion has a new message, update the last message.
                        discussion.message!.message = data.message;
                        discussion.message!.timecreated = data.timecreated;
                    }
                }
            },
            this.siteId,
        );

        // Update discussions when a message is read.
        this.readChangedObserver = CoreEvents.on(
            AddonMessagesProvider.READ_CHANGED_EVENT,
            (data) => {
                if (data.userId && this.discussions) {
                    const discussion = this.discussions.find((disc) => disc.message!.user == data.userId);

                    if (typeof discussion != 'undefined') {
                    // A discussion has been read reset counter.
                        discussion.unread = false;

                        // Conversations changed, invalidate them and refresh unread counts.
                        AddonMessages.invalidateConversations(this.siteId);
                        AddonMessages.refreshUnreadConversationCounts(this.siteId);
                    }
                }
            },
            this.siteId,
        );

        // Refresh the view when the app is resumed.
        this.appResumeSubscription = Platform.resume.subscribe(() => {
            if (!this.loaded) {
                return;
            }
            this.loaded = false;
            this.refreshData();
        });

        // If a message push notification is received, refresh the view.
        this.pushObserver = CorePushNotificationsDelegate.on<CorePushNotificationsNotificationBasicData>('receive')
            .subscribe((notification) => {
                // New message received. If it's from current site, refresh the data.
                if (CoreUtils.isFalseOrZero(notification.notif) && notification.site == this.siteId) {
                // Don't refresh unread counts, it's refreshed from the main menu handler in this case.
                    this.refreshData(undefined, false);
                }
            });
    }

    /**
     * Component loaded.
     */
    ngOnInit(): void {
        this.route.queryParams.subscribe(async (params) => {
            const discussionUserId = CoreNavigator.getRouteNumberParam('discussionUserId', { params }) ||
                CoreNavigator.getRouteNumberParam('userId', { params }) || undefined;

            if (this.loaded && this.discussionUserId == discussionUserId) {
                return;
            }

            this.discussionUserId = discussionUserId;

            if (this.discussionUserId) {
                // There is a discussion to load, open the discussion in a new state.
                this.gotoDiscussion(this.discussionUserId);
            }

            await this.fetchData();

            if (!this.discussionUserId && this.discussions.length > 0 && CoreScreen.isTablet) {
                // Take first and load it.
                this.gotoDiscussion(this.discussions[0].message!.user);
            }
        });
    }

    /**
     * Refresh the data.
     *
     * @param refresher Refresher.
     * @param refreshUnreadCounts Whteher to refresh unread counts.
     * @return Promise resolved when done.
     */
    async refreshData(refresher?: IonRefresher, refreshUnreadCounts: boolean = true): Promise<void> {
        const promises: Promise<void>[] = [];
        promises.push(AddonMessages.invalidateDiscussionsCache(this.siteId));

        if (refreshUnreadCounts) {
            promises.push(AddonMessages.invalidateUnreadConversationCounts(this.siteId));
        }

        await CoreUtils.allPromises(promises).finally(() => this.fetchData().finally(() => {
            if (refresher) {
                refresher?.complete();
            }
        }));
    }

    /**
     * Fetch discussions.
     *
     * @return Promise resolved when done.
     */
    protected async fetchData(): Promise<void> {
        this.loadingMessage = this.loadingMessages;
        this.search.enabled = AddonMessages.isSearchMessagesEnabled();

        const promises: Promise<unknown>[] = [];

        promises.push(AddonMessages.getDiscussions(this.siteId).then((discussions) => {
            // Convert to an array for sorting.
            const discussionsSorted: AddonMessagesDiscussion[] = [];
            for (const userId in discussions) {
                discussions[userId].unread = !!discussions[userId].unread;

                discussionsSorted.push(discussions[userId]);
            }

            this.discussions = discussionsSorted.sort((a, b) => (b.message?.timecreated || 0) - (a.message?.timecreated || 0));

            return;
        }));

        promises.push(AddonMessages.getUnreadConversationCounts(this.siteId));

        try {
            await Promise.all(promises);
        } catch (error) {
            CoreDomUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true);
        }

        this.loaded = true;
    }

    /**
     * Clear search and show discussions again.
     */
    clearSearch(): void {
        this.loaded = false;
        this.search.showResults = false;
        this.search.text = ''; // Reset searched string.
        this.fetchData().finally(() => {
            this.loaded = true;
        });
    }

    /**
     * Search messages cotaining text.
     *
     * @param query Text to search for.
     * @return Resolved when done.
     */
    async searchMessage(query: string): Promise<void> {
        CoreApp.closeKeyboard();
        this.loaded = false;
        this.loadingMessage = this.search.loading;

        try {
            const searchResults = await AddonMessages.searchMessages(query, undefined, undefined, undefined, this.siteId);
            this.search.showResults = true;
            this.search.results = searchResults.messages;
        } catch (error) {
            CoreDomUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingmessages', true);
        }

        this.loaded = true;
    }

    /**
     * Navigate to a particular discussion.
     *
     * @param discussionUserId Discussion Id to load.
     * @param messageId Message to scroll after loading the discussion. Used when searching.
     * @param onlyWithSplitView Only go to Discussion if split view is on.
     */
    gotoDiscussion(discussionUserId: number, messageId?: number): void {
        this.discussionUserId = discussionUserId;

        const params: Params = {
            userId: discussionUserId,
        };

        if (messageId) {
            params.message = messageId;
        }

        const splitViewLoaded = CoreNavigator.isCurrentPathInTablet('**/messages/index/discussion');
        const path = (splitViewLoaded ? '../' : '') + 'discussion';

        CoreNavigator.navigate(path, { params });
    }

    /**
     * Navigate to contacts view.
     */
    gotoContacts(): void {
        const params: Params = {};

        if (CoreScreen.isTablet && this.discussionUserId) {
            params.discussionUserId = this.discussionUserId;
        }

        CoreNavigator.navigateToSitePath('contacts-35', { params });
    }

    /**
     * Component destroyed.
     */
    ngOnDestroy(): void {
        this.newMessagesObserver?.off();
        this.readChangedObserver?.off();
        this.appResumeSubscription?.unsubscribe();
        this.pushObserver?.unsubscribe();
    }

}
