import * as workerTimers from 'worker-timers';
import configRepository from '../repository/config.js';
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
import { worldRequest } from './request';

export default class extends baseClass {
    constructor(_app, _API, _t) {
        super(_app, _API, _t);
    }

    _data = {
        sharedFeed: {
            gameLog: {
                wrist: [],
                lastEntryDate: ''
            },
            feedTable: {
                wrist: [],
                lastEntryDate: ''
            },
            notificationTable: {
                wrist: [],
                lastEntryDate: ''
            },
            friendLogTable: {
                wrist: [],
                lastEntryDate: ''
            },
            moderationAgainstTable: {
                wrist: [],
                lastEntryDate: ''
            },
            pendingUpdate: false
        },
        updateSharedFeedTimer: null,
        updateSharedFeedPending: false,
        updateSharedFeedPendingForceUpdate: false
    };

    _methods = {
        updateSharedFeed(forceUpdate) {
            if (!this.friendLogInitStatus) {
                return;
            }
            if (this.updateSharedFeedTimer) {
                if (forceUpdate) {
                    this.updateSharedFeedPendingForceUpdate = true;
                }
                this.updateSharedFeedPending = true;
            } else {
                this.updateSharedExecute(forceUpdate);
                this.updateSharedFeedTimer = setTimeout(() => {
                    if (this.updateSharedFeedPending) {
                        this.updateSharedExecute(
                            this.updateSharedFeedPendingForceUpdate
                        );
                    }
                    this.updateSharedFeedTimer = null;
                }, 150);
            }
        },

        updateSharedExecute(forceUpdate) {
            try {
                this.updateSharedFeedDebounce(forceUpdate);
            } catch (err) {
                console.error(err);
            }
            this.updateSharedFeedTimer = null;
            this.updateSharedFeedPending = false;
            this.updateSharedFeedPendingForceUpdate = false;
        },

        updateSharedFeedDebounce(forceUpdate) {
            this.updateSharedFeedGameLog(forceUpdate);
            this.updateSharedFeedFeedTable(forceUpdate);
            this.updateSharedFeedNotificationTable(forceUpdate);
            this.updateSharedFeedFriendLogTable(forceUpdate);
            this.updateSharedFeedModerationAgainstTable(forceUpdate);
            var feeds = this.sharedFeed;
            if (!feeds.pendingUpdate) {
                return;
            }
            var wristFeed = [];
            wristFeed = wristFeed.concat(
                feeds.gameLog.wrist,
                feeds.feedTable.wrist,
                feeds.notificationTable.wrist,
                feeds.friendLogTable.wrist,
                feeds.moderationAgainstTable.wrist
            );
            // OnPlayerJoining/Traveling
            API.currentTravelers.forEach((ref) => {
                var isFavorite = this.localFavoriteFriends.has(ref.id);
                if (
                    (this.sharedFeedFilters.wrist.OnPlayerJoining ===
                        'Friends' ||
                        (this.sharedFeedFilters.wrist.OnPlayerJoining ===
                            'VIP' &&
                            isFavorite)) &&
                    !$app.lastLocation.playerList.has(ref.id)
                ) {
                    if (ref.$location.tag === $app.lastLocation.location) {
                        var feedEntry = {
                            ...ref,
                            isFavorite,
                            isFriend: true,
                            type: 'OnPlayerJoining'
                        };
                        wristFeed.unshift(feedEntry);
                    } else {
                        var worldRef = API.cachedWorlds.get(
                            ref.$location.worldId
                        );
                        var groupName = '';
                        if (ref.$location.groupId) {
                            var groupRef = API.cachedGroups.get(
                                ref.$location.groupId
                            );
                            if (typeof groupRef !== 'undefined') {
                                groupName = groupRef.name;
                            } else {
                                // no group cache, fetch group and try again
                                API.getGroup({
                                    groupId: ref.$location.groupId
                                })
                                    .then((args) => {
                                        workerTimers.setTimeout(() => {
                                            // delay to allow for group cache to update
                                            $app.sharedFeed.pendingUpdate = true;
                                            $app.updateSharedFeed(false);
                                        }, 100);
                                        return args;
                                    })
                                    .catch((err) => {
                                        console.error(err);
                                    });
                            }
                        }
                        if (typeof worldRef !== 'undefined') {
                            var feedEntry = {
                                created_at: ref.created_at,
                                type: 'GPS',
                                userId: ref.id,
                                displayName: ref.displayName,
                                location: ref.$location.tag,
                                worldName: worldRef.name,
                                groupName,
                                previousLocation: '',
                                isFavorite,
                                time: 0,
                                isFriend: true,
                                isTraveling: true
                            };
                            wristFeed.unshift(feedEntry);
                        } else {
                            // no world cache, fetch world and try again
                            worldRequest
                                .getWorld({
                                    worldId: ref.$location.worldId
                                })
                                .then((args) => {
                                    workerTimers.setTimeout(() => {
                                        // delay to allow for world cache to update
                                        $app.sharedFeed.pendingUpdate = true;
                                        $app.updateSharedFeed(false);
                                    }, 100);
                                    return args;
                                })
                                .catch((err) => {
                                    console.error(err);
                                });
                        }
                    }
                }
            });
            wristFeed.sort(function (a, b) {
                if (a.created_at < b.created_at) {
                    return 1;
                }
                if (a.created_at > b.created_at) {
                    return -1;
                }
                return 0;
            });
            wristFeed.splice(16);
            AppApi.ExecuteVrFeedFunction(
                'wristFeedUpdate',
                JSON.stringify(wristFeed)
            );
            this.applyUserDialogLocation();
            this.applyWorldDialogInstances();
            this.applyGroupDialogInstances();
            feeds.pendingUpdate = false;
        },

        updateSharedFeedGameLog(forceUpdate) {
            // Location, OnPlayerJoined, OnPlayerLeft
            var sessionTable = this.gameLogSessionTable;
            var i = sessionTable.length;
            if (i > 0) {
                if (
                    sessionTable[i - 1].created_at ===
                        this.sharedFeed.gameLog.lastEntryDate &&
                    forceUpdate === false
                ) {
                    return;
                }
                this.sharedFeed.gameLog.lastEntryDate =
                    sessionTable[i - 1].created_at;
            } else {
                return;
            }
            var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
            var wristArr = [];
            var w = 0;
            var wristFilter = this.sharedFeedFilters.wrist;
            var currentUserLeaveTime = 0;
            var locationJoinTime = 0;
            for (var i = sessionTable.length - 1; i > -1; i--) {
                var ctx = sessionTable[i];
                if (ctx.created_at < bias) {
                    break;
                }
                if (ctx.type === 'Notification') {
                    continue;
                }
                // on Location change remove OnPlayerLeft
                if (ctx.type === 'LocationDestination') {
                    currentUserLeaveTime = Date.parse(ctx.created_at);
                    var currentUserLeaveTimeOffset =
                        currentUserLeaveTime + 5 * 1000;
                    for (var k = w - 1; k > -1; k--) {
                        var feedItem = wristArr[k];
                        if (
                            (feedItem.type === 'OnPlayerLeft' ||
                                feedItem.type === 'BlockedOnPlayerLeft' ||
                                feedItem.type === 'MutedOnPlayerLeft') &&
                            Date.parse(feedItem.created_at) >=
                                currentUserLeaveTime &&
                            Date.parse(feedItem.created_at) <=
                                currentUserLeaveTimeOffset
                        ) {
                            wristArr.splice(k, 1);
                            w--;
                        }
                    }
                }
                // on Location change remove OnPlayerJoined
                if (ctx.type === 'Location') {
                    locationJoinTime = Date.parse(ctx.created_at);
                    var locationJoinTimeOffset = locationJoinTime + 20 * 1000;
                    for (var k = w - 1; k > -1; k--) {
                        var feedItem = wristArr[k];
                        if (
                            (feedItem.type === 'OnPlayerJoined' ||
                                feedItem.type === 'BlockedOnPlayerJoined' ||
                                feedItem.type === 'MutedOnPlayerJoined') &&
                            Date.parse(feedItem.created_at) >=
                                locationJoinTime &&
                            Date.parse(feedItem.created_at) <=
                                locationJoinTimeOffset
                        ) {
                            wristArr.splice(k, 1);
                            w--;
                        }
                    }
                }
                // remove current user
                if (
                    (ctx.type === 'OnPlayerJoined' ||
                        ctx.type === 'OnPlayerLeft' ||
                        ctx.type === 'PortalSpawn') &&
                    ctx.displayName === API.currentUser.displayName
                ) {
                    continue;
                }
                var isFriend = false;
                var isFavorite = false;
                if (ctx.userId) {
                    isFriend = this.friends.has(ctx.userId);
                    isFavorite = this.localFavoriteFriends.has(ctx.userId);
                } else if (ctx.displayName) {
                    for (var ref of API.cachedUsers.values()) {
                        if (ref.displayName === ctx.displayName) {
                            isFriend = this.friends.has(ref.id);
                            isFavorite = this.localFavoriteFriends.has(ref.id);
                            break;
                        }
                    }
                }
                // add tag colour
                var tagColour = '';
                if (ctx.userId) {
                    var tagRef = this.customUserTags.get(ctx.userId);
                    if (typeof tagRef !== 'undefined') {
                        tagColour = tagRef.colour;
                    }
                }
                // BlockedOnPlayerJoined, BlockedOnPlayerLeft, MutedOnPlayerJoined, MutedOnPlayerLeft
                if (
                    ctx.type === 'OnPlayerJoined' ||
                    ctx.type === 'OnPlayerLeft'
                ) {
                    for (var ref of API.cachedPlayerModerations.values()) {
                        if (
                            ref.targetDisplayName !== ctx.displayName &&
                            ref.sourceUserId !== ctx.userId
                        ) {
                            continue;
                        }

                        if (ref.type === 'block') {
                            var type = `Blocked${ctx.type}`;
                        } else if (ref.type === 'mute') {
                            var type = `Muted${ctx.type}`;
                        } else {
                            continue;
                        }

                        var entry = {
                            created_at: ctx.created_at,
                            type,
                            displayName: ref.targetDisplayName,
                            userId: ref.targetUserId,
                            isFriend,
                            isFavorite
                        };
                        if (
                            wristFilter[type] &&
                            (wristFilter[type] === 'Everyone' ||
                                (wristFilter[type] === 'Friends' && isFriend) ||
                                (wristFilter[type] === 'VIP' && isFavorite))
                        ) {
                            wristArr.unshift(entry);
                        }
                        this.queueGameLogNoty(entry);
                    }
                }
                // when too many user joins happen at once when switching instances
                // the "w" counter maxes out and wont add any more entries
                // until the onJoins are cleared by "Location"
                // e.g. if a "VideoPlay" occurs between "OnPlayerJoined" and "Location" it wont be added
                if (
                    w < 50 &&
                    wristFilter[ctx.type] &&
                    (wristFilter[ctx.type] === 'On' ||
                        wristFilter[ctx.type] === 'Everyone' ||
                        (wristFilter[ctx.type] === 'Friends' && isFriend) ||
                        (wristFilter[ctx.type] === 'VIP' && isFavorite))
                ) {
                    wristArr.push({
                        ...ctx,
                        tagColour,
                        isFriend,
                        isFavorite
                    });
                    ++w;
                }
            }
            this.sharedFeed.gameLog.wrist = wristArr;
            this.sharedFeed.pendingUpdate = true;
        },

        updateSharedFeedFeedTable(forceUpdate) {
            // GPS, Online, Offline, Status, Avatar
            var feedSession = this.feedSessionTable;
            var i = feedSession.length;
            if (i > 0) {
                if (
                    feedSession[i - 1].created_at ===
                        this.sharedFeed.feedTable.lastEntryDate &&
                    forceUpdate === false
                ) {
                    return;
                }
                this.sharedFeed.feedTable.lastEntryDate =
                    feedSession[i - 1].created_at;
            } else {
                return;
            }
            var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
            var wristArr = [];
            var w = 0;
            var wristFilter = this.sharedFeedFilters.wrist;
            for (var i = feedSession.length - 1; i > -1; i--) {
                var ctx = feedSession[i];
                if (ctx.created_at < bias) {
                    break;
                }
                if (ctx.type === 'Avatar') {
                    continue;
                }
                // hide private worlds from feed
                if (
                    this.hidePrivateFromFeed &&
                    ctx.type === 'GPS' &&
                    ctx.location === 'private'
                ) {
                    continue;
                }
                var isFriend = this.friends.has(ctx.userId);
                var isFavorite = this.localFavoriteFriends.has(ctx.userId);
                if (
                    w < 20 &&
                    wristFilter[ctx.type] &&
                    (wristFilter[ctx.type] === 'Friends' ||
                        (wristFilter[ctx.type] === 'VIP' && isFavorite))
                ) {
                    wristArr.push({
                        ...ctx,
                        isFriend,
                        isFavorite
                    });
                    ++w;
                }
            }
            this.sharedFeed.feedTable.wrist = wristArr;
            this.sharedFeed.pendingUpdate = true;
        },

        updateSharedFeedNotificationTable(forceUpdate) {
            // invite, requestInvite, requestInviteResponse, inviteResponse, friendRequest
            var notificationTable = this.notificationTable.data;
            var i = notificationTable.length;
            if (i > 0) {
                if (
                    notificationTable[i - 1].created_at ===
                        this.sharedFeed.notificationTable.lastEntryDate &&
                    forceUpdate === false
                ) {
                    return;
                }
                this.sharedFeed.notificationTable.lastEntryDate =
                    notificationTable[i - 1].created_at;
            } else {
                return;
            }
            var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
            var wristArr = [];
            var w = 0;
            var wristFilter = this.sharedFeedFilters.wrist;
            for (var i = notificationTable.length - 1; i > -1; i--) {
                var ctx = notificationTable[i];
                if (ctx.created_at < bias) {
                    break;
                }
                if (ctx.senderUserId === API.currentUser.id) {
                    continue;
                }
                var isFriend = this.friends.has(ctx.senderUserId);
                var isFavorite = this.localFavoriteFriends.has(
                    ctx.senderUserId
                );
                if (
                    w < 20 &&
                    wristFilter[ctx.type] &&
                    (wristFilter[ctx.type] === 'On' ||
                        wristFilter[ctx.type] === 'Friends' ||
                        (wristFilter[ctx.type] === 'VIP' && isFavorite))
                ) {
                    wristArr.push({
                        ...ctx,
                        isFriend,
                        isFavorite
                    });
                    ++w;
                }
            }
            this.sharedFeed.notificationTable.wrist = wristArr;
            this.sharedFeed.pendingUpdate = true;
        },

        updateSharedFeedFriendLogTable(forceUpdate) {
            // TrustLevel, Friend, FriendRequest, Unfriend, DisplayName
            var friendLog = this.friendLogTable.data;
            var i = friendLog.length;
            if (i > 0) {
                if (
                    friendLog[i - 1].created_at ===
                        this.sharedFeed.friendLogTable.lastEntryDate &&
                    forceUpdate === false
                ) {
                    return;
                }
                this.sharedFeed.friendLogTable.lastEntryDate =
                    friendLog[i - 1].created_at;
            } else {
                return;
            }
            var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
            var wristArr = [];
            var w = 0;
            var wristFilter = this.sharedFeedFilters.wrist;
            for (var i = friendLog.length - 1; i > -1; i--) {
                var ctx = friendLog[i];
                if (ctx.created_at < bias) {
                    break;
                }
                if (ctx.type === 'FriendRequest') {
                    continue;
                }
                var isFriend = this.friends.has(ctx.userId);
                var isFavorite = this.localFavoriteFriends.has(ctx.userId);
                if (
                    w < 20 &&
                    wristFilter[ctx.type] &&
                    (wristFilter[ctx.type] === 'On' ||
                        wristFilter[ctx.type] === 'Friends' ||
                        (wristFilter[ctx.type] === 'VIP' && isFavorite))
                ) {
                    wristArr.push({
                        ...ctx,
                        isFriend,
                        isFavorite
                    });
                    ++w;
                }
            }
            this.sharedFeed.friendLogTable.wrist = wristArr;
            this.sharedFeed.pendingUpdate = true;
        },

        updateSharedFeedModerationAgainstTable(forceUpdate) {
            // Unblocked, Blocked, Muted, Unmuted
            var moderationAgainst = this.moderationAgainstTable;
            var i = moderationAgainst.length;
            if (i > 0) {
                if (
                    moderationAgainst[i - 1].created_at ===
                        this.sharedFeed.moderationAgainstTable.lastEntryDate &&
                    forceUpdate === false
                ) {
                    return;
                }
                this.sharedFeed.moderationAgainstTable.lastEntryDate =
                    moderationAgainst[i - 1].created_at;
            } else {
                return;
            }
            var bias = new Date(Date.now() - 86400000).toJSON(); // 24 hours
            var wristArr = [];
            var w = 0;
            var wristFilter = this.sharedFeedFilters.wrist;
            for (var i = moderationAgainst.length - 1; i > -1; i--) {
                var ctx = moderationAgainst[i];
                if (ctx.created_at < bias) {
                    break;
                }
                var isFriend = this.friends.has(ctx.userId);
                var isFavorite = this.localFavoriteFriends.has(ctx.userId);
                // add tag colour
                var tagColour = '';
                var tagRef = this.customUserTags.get(ctx.userId);
                if (typeof tagRef !== 'undefined') {
                    tagColour = tagRef.colour;
                }
                if (
                    w < 20 &&
                    wristFilter[ctx.type] &&
                    wristFilter[ctx.type] === 'On'
                ) {
                    wristArr.push({
                        ...ctx,
                        isFriend,
                        isFavorite,
                        tagColour
                    });
                    ++w;
                }
            }
            this.sharedFeed.moderationAgainstTable.wrist = wristArr;
            this.sharedFeed.pendingUpdate = true;
        },

        saveSharedFeedFilters() {
            configRepository.setString(
                'sharedFeedFilters',
                JSON.stringify(this.sharedFeedFilters)
            );
            this.updateSharedFeed(true);
        },

        async resetNotyFeedFilters() {
            this.sharedFeedFilters.noty = {
                ...this.sharedFeedFiltersDefaults.noty
            };
            this.saveSharedFeedFilters();
        },

        async resetWristFeedFilters() {
            this.sharedFeedFilters.wrist = {
                ...this.sharedFeedFiltersDefaults.wrist
            };
            this.saveSharedFeedFilters();
        }
    };
}
