import { type Instance, render } from "ink";

import { FullScreenBox } from "./FullScreenBox.js";

type InkRender = typeof render;
type WithFullScreen = (...args: Parameters<InkRender>) => {
	instance: Instance;
	start: () => Promise<void>;
	waitUntilExit: () => Promise<void>;
};

export async function enterAlternateBuffer() {
	await write("\x1b[?1049h"); // enter alternate buffer
}

export async function exitAlternateBuffer() {
	await write("\x1b[?1049l"); // exit alternate buffer
}

export async function write(content: string) {
	return new Promise<void>((resolve, reject) => {
		process.stdout.write(content, (error) => {
			if (error) reject(error);
			else resolve();
		});
	});
}

async function cleanUpOnExit(instance: Instance) {
	await instance.waitUntilExit();
	restoreStdin();
	await exitAlternateBuffer();
}

function restoreStdin() {
	let { stdin } = process;
	if (!stdin.isTTY) {
		return;
	}
	if (typeof stdin.setRawMode === "function") {
		stdin.setRawMode(false);
	}
	stdin.pause();
}

export const withFullScreen: WithFullScreen = (node, options) => {
	const instance = render(null, options);
	const exitPromise = cleanUpOnExit(instance);
	function waitUntilExit() {
		return exitPromise;
	}

	return {
		instance: instance,
		start: async () => {
			await enterAlternateBuffer();
			instance.rerender(<FullScreenBox>{node}</FullScreenBox>);
		},
		waitUntilExit,
	};
};
