import { playbackManager } from './playback/playbackmanager';
import serverNotifications from '../scripts/serverNotifications';
import { Events } from 'jellyfin-apiclient';

function onUserDataChanged() {
    const instance = this;

    const eventsToMonitor = getEventsToMonitor(instance);

    // TODO: Check user data change reason?
    if (eventsToMonitor.indexOf('markfavorite') !== -1) {
        instance.notifyRefreshNeeded();
    } else if (eventsToMonitor.indexOf('markplayed') !== -1) {
        instance.notifyRefreshNeeded();
    }
}

function getEventsToMonitor(instance) {
    const options = instance.options;
    const monitor = options ? options.monitorEvents : null;
    if (monitor) {
        return monitor.split(',');
    }

    return [];
}

function onTimerCreated() {
    const instance = this;

    if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
        instance.notifyRefreshNeeded();
        return;
    }
}

function onSeriesTimerCreated() {
    const instance = this;
    if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
        instance.notifyRefreshNeeded();
        return;
    }
}

function onTimerCancelled() {
    const instance = this;

    if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
        instance.notifyRefreshNeeded();
        return;
    }
}

function onSeriesTimerCancelled() {
    const instance = this;
    if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
        instance.notifyRefreshNeeded();
        return;
    }
}

function onLibraryChanged(e, apiClient, data) {
    const instance = this;
    const eventsToMonitor = getEventsToMonitor(instance);
    if (eventsToMonitor.indexOf('seriestimers') !== -1 || eventsToMonitor.indexOf('timers') !== -1) {
        // yes this is an assumption
        return;
    }

    const itemsAdded = data.ItemsAdded || [];
    const itemsRemoved = data.ItemsRemoved || [];
    if (!itemsAdded.length && !itemsRemoved.length) {
        return;
    }

    const options = instance.options || {};
    const parentId = options.parentId;
    if (parentId) {
        const foldersAddedTo = data.FoldersAddedTo || [];
        const foldersRemovedFrom = data.FoldersRemovedFrom || [];
        const collectionFolders = data.CollectionFolders || [];

        if (foldersAddedTo.indexOf(parentId) === -1 && foldersRemovedFrom.indexOf(parentId) === -1 && collectionFolders.indexOf(parentId) === -1) {
            return;
        }
    }

    instance.notifyRefreshNeeded();
}

function onPlaybackStopped(e, stopInfo) {
    const instance = this;

    const state = stopInfo.state;

    const eventsToMonitor = getEventsToMonitor(instance);
    if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') {
        if (eventsToMonitor.indexOf('videoplayback') !== -1) {
            instance.notifyRefreshNeeded(true);
            return;
        }
    } else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') {
        if (eventsToMonitor.indexOf('audioplayback') !== -1) {
            instance.notifyRefreshNeeded(true);
            return;
        }
    }
}

function addNotificationEvent(instance, name, handler, owner) {
    const localHandler = handler.bind(instance);
    owner = owner || serverNotifications;
    Events.on(owner, name, localHandler);
    instance['event_' + name] = localHandler;
}

function removeNotificationEvent(instance, name, owner) {
    const handler = instance['event_' + name];
    if (handler) {
        owner = owner || serverNotifications;
        Events.off(owner, name, handler);
        instance['event_' + name] = null;
    }
}

class ItemsRefresher {
    constructor(options) {
        this.options = options || {};

        addNotificationEvent(this, 'UserDataChanged', onUserDataChanged);
        addNotificationEvent(this, 'TimerCreated', onTimerCreated);
        addNotificationEvent(this, 'SeriesTimerCreated', onSeriesTimerCreated);
        addNotificationEvent(this, 'TimerCancelled', onTimerCancelled);
        addNotificationEvent(this, 'SeriesTimerCancelled', onSeriesTimerCancelled);
        addNotificationEvent(this, 'LibraryChanged', onLibraryChanged);
        addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager);
    }

    pause() {
        clearRefreshInterval(this, true);

        this.paused = true;
    }

    resume(options) {
        this.paused = false;

        const refreshIntervalEndTime = this.refreshIntervalEndTime;
        if (refreshIntervalEndTime) {
            const remainingMs = refreshIntervalEndTime - new Date().getTime();
            if (remainingMs > 0 && !this.needsRefresh) {
                resetRefreshInterval(this, remainingMs);
            } else {
                this.needsRefresh = true;
                this.refreshIntervalEndTime = null;
            }
        }

        if (this.needsRefresh || (options && options.refresh)) {
            return this.refreshItems();
        }

        return Promise.resolve();
    }

    refreshItems() {
        if (!this.fetchData) {
            return Promise.resolve();
        }

        if (this.paused) {
            this.needsRefresh = true;
            return Promise.resolve();
        }

        this.needsRefresh = false;

        return this.fetchData().then(onDataFetched.bind(this));
    }

    notifyRefreshNeeded(isInForeground) {
        if (this.paused) {
            this.needsRefresh = true;
            return;
        }

        const timeout = this.refreshTimeout;
        if (timeout) {
            clearTimeout(timeout);
        }

        if (isInForeground === true) {
            this.refreshItems();
        } else {
            this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 10000);
        }
    }

    destroy() {
        clearRefreshInterval(this);

        removeNotificationEvent(this, 'UserDataChanged');
        removeNotificationEvent(this, 'TimerCreated');
        removeNotificationEvent(this, 'SeriesTimerCreated');
        removeNotificationEvent(this, 'TimerCancelled');
        removeNotificationEvent(this, 'SeriesTimerCancelled');
        removeNotificationEvent(this, 'LibraryChanged');
        removeNotificationEvent(this, 'playbackstop', playbackManager);

        this.fetchData = null;
        this.options = null;
    }
}

function clearRefreshInterval(instance, isPausing) {
    if (instance.refreshInterval) {
        clearInterval(instance.refreshInterval);
        instance.refreshInterval = null;

        if (!isPausing) {
            instance.refreshIntervalEndTime = null;
        }
    }
}

function resetRefreshInterval(instance, intervalMs) {
    clearRefreshInterval(instance);

    if (!intervalMs) {
        const options = instance.options;
        if (options) {
            intervalMs = options.refreshIntervalMs;
        }
    }

    if (intervalMs) {
        instance.refreshInterval = setInterval(instance.notifyRefreshNeeded.bind(instance), intervalMs);
        instance.refreshIntervalEndTime = new Date().getTime() + intervalMs;
    }
}

function onDataFetched(result) {
    resetRefreshInterval(this);

    if (this.afterRefresh) {
        this.afterRefresh(result);
    }
}

export default ItemsRefresher;
