import * as fs from "node:fs";
import * as path from "node:path";
import { SSH_PATH } from "@/server/constants";
import { spawnAsync } from "../process/spawnAsync";

const readSSHKey = async (id: string) => {
	try {
		if (!fs.existsSync(SSH_PATH)) {
			fs.mkdirSync(SSH_PATH, { recursive: true });
		}

		return {
			privateKey: fs.readFileSync(path.join(SSH_PATH, `${id}_rsa`), {
				encoding: "utf-8",
			}),
			publicKey: fs.readFileSync(path.join(SSH_PATH, `${id}_rsa.pub`), {
				encoding: "utf-8",
			}),
		};
	} catch (error) {
		throw error;
	}
};

export const saveSSHKey = async (
	id: string,
	publicKey: string,
	privateKey: string,
) => {
	const applicationDirectory = SSH_PATH;

	const privateKeyPath = path.join(applicationDirectory, `${id}_rsa`);
	const publicKeyPath = path.join(applicationDirectory, `${id}_rsa.pub`);

	const privateKeyStream = fs.createWriteStream(privateKeyPath, {
		mode: 0o600,
	});
	privateKeyStream.write(privateKey);
	privateKeyStream.end();

	fs.writeFileSync(publicKeyPath, publicKey);
};

export const generateSSHKey = async (type: "rsa" | "ed25519" = "rsa") => {
	const applicationDirectory = SSH_PATH;

	if (!fs.existsSync(applicationDirectory)) {
		fs.mkdirSync(applicationDirectory, { recursive: true });
	}

	const keyPath = path.join(applicationDirectory, "temp_rsa");

	if (fs.existsSync(`${keyPath}`)) {
		fs.unlinkSync(`${keyPath}`);
	}

	if (fs.existsSync(`${keyPath}.pub`)) {
		fs.unlinkSync(`${keyPath}.pub`);
	}

	const args = [
		"-t",
		type,
		"-b",
		"4096",
		"-C",
		"dokploy",
		"-m",
		"PEM",
		"-f",
		keyPath,
		"-N",
		"",
	];

	try {
		await spawnAsync("ssh-keygen", args);
		const data = await readSSHKey("temp");
		await removeSSHKey("temp");
		return data;
	} catch (error) {
		throw error;
	}
};

export const removeSSHKey = async (id: string) => {
	try {
		const publicKeyPath = path.join(SSH_PATH, `${id}_rsa.pub`);
		const privateKeyPath = path.join(SSH_PATH, `${id}_rsa`);
		await fs.promises.unlink(publicKeyPath);
		await fs.promises.unlink(privateKeyPath);
	} catch (error) {
		throw error;
	}
};
