// On mobile all the setTimeout and setInterval should go through this class
// as it will either use the native timeout/interval for short intervals or
// the custom one for long intervals.

// For custom intervals, they are triggered
// whenever the update() function is called, and in mobile it's called for
// example on the Redux action middleware or when the app gets focus.

import { Hour } from '@joplin/utils/time';
import time from './time';

type IntervalId = number;

interface Interval {
	id: IntervalId;
	// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
	callback: Function;
	interval: number;
	lastIntervalTime: number;
	isTimeout: boolean;
}

interface Intervals {
	[key: number]: Interval;
}

export default class PoorManIntervals {

	// We disable the custom logic since React Native no longer emit a warning for long timer, and
	// using long timers is fine as long as we are fine with it not being triggered while the app is
	// in the background:
	// https://github.com/facebook/react-native/issues/12981#issuecomment-652745831

	private static maxNativeTimerDuration_ = 24 * Hour; // 10 * 1000;
	private static lastUpdateTime_ = 0;
	private static intervalId_: IntervalId = 0;
	private static intervals_: Intervals = {};

	// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
	public static setInterval(callback: Function, interval: number): IntervalId {
		if (interval <= this.maxNativeTimerDuration_) return setInterval(callback, interval);

		this.intervalId_++;
		const id = this.intervalId_;

		this.intervals_[id] = {
			id: id,
			callback: callback,
			interval: interval,
			lastIntervalTime: time.unixMs(),
			isTimeout: false,
		};

		return id;
	}

	// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
	public static setTimeout(callback: Function, interval: number): IntervalId {
		if (interval <= this.maxNativeTimerDuration_) return setTimeout(callback, interval);

		this.intervalId_++;
		const id = this.intervalId_;

		this.intervals_[id] = {
			id: id,
			callback: callback,
			interval: interval,
			lastIntervalTime: time.unixMs(),
			isTimeout: true,
		};

		return id;
	}

	public static clearInterval(id: IntervalId) {
		const r = this.intervals_[id];
		if (!r) {
			clearInterval(id);
		} else {
			delete this.intervals_[id];
		}
	}

	public static clearTimeout(id: IntervalId) {
		const r = this.intervals_[id];
		if (!r) {
			clearTimeout(id);
		} else {
			delete this.intervals_[id];
		}
	}

	public static update() {
		// Don't update more than once a second
		if (this.lastUpdateTime_ + 1000 > time.unixMs()) return;

		for (const id in this.intervals_) {
			const interval = this.intervals_[id];
			const now = time.unixMs();
			if (now - interval.lastIntervalTime >= interval.interval) {
				interval.lastIntervalTime = now;
				interval.callback();
				if (interval.isTimeout) {
					this.clearTimeout(interval.id);
				}
			}
		}

		this.lastUpdateTime_ = time.unixMs();
	}
}
