interface Capture {
	showPlayback: boolean;
	isRecording: boolean;
	isDone: boolean;
	startRecord(): void;
	mints: number;
	maxts: number;
	min: number;
	minthumb: number;
	maxthumb: number;
	mintrigger(): void;
	maxtrigger(): void;
	init(): void;
	play(): void;
	pause(): void;
	duration: number;
}

document.body.addEventListener("htmx:afterSwap", (e) => {
	if (window.Alpine) {
		if (e.target instanceof HTMLElement) {
			window.Alpine.initTree(e.target);
		}
	}
});

function registerCapture() {
	if (!window.Alpine) return;

	Alpine.data(
		"capture",
		(): Capture => ({
			showPlayback: false,
			isDone: false,
			isRecording: false,
			mints: 0,
			maxts: 0,
			min: 0,
			minthumb: 0,
			maxthumb: 0,
			duration: 3,

			play() {
				const self = this as any;
				self.$rec.play();
			},
			pause() {
				const self = this as any;
				self.$rec.pause();
			},

			init() {
				const self = this as any;
				const rec = self.$refs.record;

				this.maxts = this.duration;

				self.$watch("mints", (value: number) => {
					if (rec) {
						rec.currentTime = value;
					}
				});
				self.$watch("maxts", (value: number) => {
					if (rec) {
						rec.currentTime = value;
					}
				});

				rec.onplay = () => {
					rec.currentTime = this.mints;
				};
				const uCurrentMax = () => {
					return this.maxts;
				};
				const currentMax = uCurrentMax.bind(this);

				rec.ontimeupdate = () => {
					if (!rec.paused) {
						console.log(rec.currentTime);
						if (currentMax() <= rec.currentTime) rec.pause();
					}
				};

				self.mintrigger();
				self.maxtrigger();
			},

			async startRecord() {
				if (!this.isRecording) {
					const self = this as any;
					this.showPlayback = false;
					const stream = await createStream();
					this.isRecording = true;

					if (self.$refs.preview.srcObject) {
						self.$refs.preview.srcObject = null;
					}
					if (self.$refs.record.srcObject) {
						self.$refs.record.srcObject = null;
					}

					self.$refs.preview.srcObject = stream;

					self.$refs.preview.onloadedmetadata = () => {
						self.$refs.preview.play();
					};

					self.$refs.preview.load();

					const chunks = await record(stream, this.duration * 1000);

					const dt = new DataTransfer();
					const file = new File(chunks, createFileName(), {
						type: "video/webm",
					});

					stream.getTracks().forEach((t) => t.stop());
					self.$refs.preview.pause();

					dt.items.add(file);

					const blob = new Blob(chunks);

					self.$refs.record.src = URL.createObjectURL(blob);
					self.$refs.record.load();

					this.showPlayback = true;
					this.isDone = true;
					this.isRecording = false;

					self.$refs.fileInput.files = dt.files;
				}
			},

			mintrigger() {
				this.mints = Math.min(this.mints, this.maxts - 1);
				this.minthumb =
					((this.mints - this.min) / (this.duration - this.min)) * 100;
			},

			maxtrigger() {
				this.maxts = Math.max(this.maxts, this.mints + 1);
				this.maxthumb =
					100 - ((this.maxts - this.min) / (this.duration - this.min)) * 100;
			},
		}),
	);
}

if (window.Alpine) {
	registerCapture();
} else {
	document.addEventListener("alpine:init", registerCapture);
}

export const createStream = async (): Promise<MediaStream> => {
	return await navigator.mediaDevices.getUserMedia({
		audio: false,
		video: { facingMode: "user" },
	});
};

export const createFileName = () => {
	return `cap_${Date.now()}.webm`;
};

export async function record(
	stream: MediaStream,
	maxDuration = 10000,
): Promise<Blob[]> {
	const chunks: Blob[] = [];
	const rec = new MediaRecorder(stream);
	return new Promise<Blob[]>((resolve, reject) => {
		rec.ondataavailable = (e) => {
			chunks.push(e.data);
		};

		rec.start();

		setTimeout(() => {
			rec.stop();
		}, maxDuration);

		rec.onstop = () => {
			resolve(chunks);
		};
	});
}

export const fileInput = () => {
	const fileInput = document.createElement("input");
	fileInput.type = "file";
	fileInput.name = "video";
	fileInput.accept = "video/*";

	return fileInput;
};
