import { cancel, debounce } from "./index";
import test from "ava";

test("test debounce with default 500 millisecond interval", async (t) => {
	async function debounceWithDefault() {
		let triggerTimes = 0;

		class Foo {
			@debounce({ leading: true })
			static bar() {
				triggerTimes += 1;
			}
		}

		setInterval(() => {
			Foo.bar();
		}, 1);

		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve(triggerTimes);
			}, 500);
		});
	}

	t.is(await debounceWithDefault(), 1);
});

test("test debounce with time", async (t) => {
	async function debounceWithTime() {
		let triggerTimes = 0;

		class Foo {
			@debounce(1000, { leading: true })
			static bar() {
				triggerTimes += 1;
			}
		}

		setInterval(() => {
			Foo.bar();
		}, 1);

		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve(triggerTimes);
			}, 1000);
		});
	}

	t.is(await debounceWithTime(), 1);
});

test("test debounce with true leading", async (t) => {
	let triggerTimes = 0;

	class Foo {
		@debounce({ leading: true })
		static bar() {
			triggerTimes += 1;
		}
	}

	Foo.bar();

	t.is(triggerTimes, 1);
});

test("test debounce with false leading", async (t) => {
	async function debounceWithFalseLeading() {
		let triggerTimes = 0;

		class Foo {
			@debounce
			static bar() {
				triggerTimes += 1;
			}
		}

		Foo.bar();

		return new Promise((resolve, reject) => {
			resolve(triggerTimes);
		});
	}

	t.is(await debounceWithFalseLeading(), 0);
});

test("test debounce with true leading as multi-times trigger", async (t) => {
	async function debounceWithMultiTimesTrigger() {
		let triggerTimes = 0;

		class Foo {
			@debounce({ leading: true })
			static bar() {
				triggerTimes += 1;
			}
		}

		let callTimes = 0;
		const timer = setInterval(() => {
			if (callTimes < 100) {
				Foo.bar();
				callTimes += 1;
			} else {
				clearInterval(timer);
			}
		}, 10);

		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve(triggerTimes);
			}, 500 * 2 + 100);
		});
	}

	t.is(await debounceWithMultiTimesTrigger(), 1);
});

test("test debounce with false leading as multi-times trigger", async (t) => {
	async function debounceWithMultiTimesTrigger() {
		let triggerTimes = 0;

		class Foo {
			@debounce
			static bar() {
				triggerTimes += 1;
			}
		}

		let callTimes = 0;
		const timer = setInterval(() => {
			if (callTimes < 100) {
				Foo.bar();
				callTimes += 1;
			} else {
				clearInterval(timer);
			}
		}, 10);

		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve(triggerTimes);
			}, 1000 + 500 + 100);
		});
	}

	t.is(await debounceWithMultiTimesTrigger(), 1);
});

test("test debounce with method", (t) => {
	function debounceWithPropertyMethod() {
		class Foo {
			selfPointer: any;
			triggerTime: number = 0;

			@debounce({ leading: true })
			bar() {
				this.selfPointer = this;
				this.triggerTime += 1;
			}
		}

		const foo = new Foo();
		foo.bar();

		return foo;
	}

	const foo = debounceWithPropertyMethod();

	t.truthy(foo.selfPointer === foo);
	t.is(foo.triggerTime, 1);
});

test("test debounce with property method", (t) => {
	function debounceWithPropertyMethod() {
		class Foo {
			selfPointer: any;
			triggerTime: number = 0;

			@debounce({ leading: true })
			bar = () => {
				this.selfPointer = this;
				this.triggerTime += 1;
			};
		}

		const foo = new Foo();
		foo.bar();

		return foo;
	}

	const foo = debounceWithPropertyMethod();

	t.truthy(foo.selfPointer === foo);
	t.is(foo.triggerTime, 1);
});

test("test debounce cancellation", async (t) => {
	async function debounceWithCancellation() {
		let triggerTimes = 0;

		class Foo {
			@debounce({ leading: false })
			static bar() {
				triggerTimes += 1;
			}
		}

		Foo.bar();

		setTimeout(() => {
			cancel(Foo.bar);
		}, 400);

		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve(triggerTimes);
			}, 600);
		});
	}

	t.is(await debounceWithCancellation(), 0);
});

test("test debounce cancellation with property method", async (t) => {
	async function debounceWithCancellation() {
		let triggerTimes = 0;

		class Foo {
			@debounce({ leading: false })
			bar = () => {
				triggerTimes += 1;
			};
		}

		const foo = new Foo();
		foo.bar();

		setTimeout(() => {
			cancel(foo.bar);
		}, 400);

		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve(triggerTimes);
			}, 600);
		});
	}

	t.is(await debounceWithCancellation(), 0);
});
