const fs = require("fs");
const path = require("path");
const { exec } = require("node:child_process");
const { ECDH } = require("crypto");

const SAMPLE = path.join(__dirname, "..", "samples", "genericfemale.wav");
const WORDS_FILE = path.join(__dirname, "..", "words.txt");
const SOURCE_DIR = path.join(__dirname, "..", "source");
const OUT_DIR = path.join(__dirname, "..", "out");
const TTS_MODEL = "tts_models/multilingual/multi-dataset/xtts_v2";
const TTS_COMMAND = `tts --text "$word" --model_name $model --speaker_wav "$sample" --language_idx en --out_path "$outPath"`;
const FFMPEG_COMMAND = `ffmpeg -i "$file" -filter:a "volume=2.5, atempo=1.65" "$outPath"`;

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

const temp = SAMPLE.replace(".wav", "").split("\\");
const sampleName = temp.at(-1);
const OUTPUT = path.join(OUT_DIR, sampleName);
const SOURCE = path.join(SOURCE_DIR, sampleName);
if (!fs.existsSync(OUTPUT)) {
	fs.mkdirSync(OUTPUT, { recursive: true });
}
if (!fs.existsSync(SOURCE)) {
	fs.mkdirSync(SOURCE, { recursive: true });
}

const words = fs
	.readFileSync(WORDS_FILE)
	.toString()
	.split("\n")
	.filter((w) => !w.includes("#"));
function getCookedFiles() {
	return fs.readdirSync(OUTPUT).map((f) => f.replace(".ogg", ""));
}
function getSourceFiles() {
	return fs.readdirSync(SOURCE).map((f) => f.replace(".wav", ""));
}

function execCommand(command) {
	return new Promise((resolve, reject) => {
		exec(command, (error, stdout, stderr) => {
			if (error) {
				console.error("Error running TTS: " + stderr);
				reject(stderr);
			}
			resolve(stdout);
		});
	});
}
async function runTTS(word) {
	const ttsComamnd = TTS_COMMAND.replace("$word", word)
		.replace("$model", TTS_MODEL)
		.replace("$sample", SAMPLE)
		.replace("$outPath", `${path.join(SOURCE, word + ".wav")}`);
	console.log(`Running tts for ${word} using ${ttsComamnd}`);
	await execCommand(ttsComamnd);
}
async function doCommit() {
	const tempLookup = getCookedFiles().reduce((acc, file) => {
		acc[file] = true;
		return acc;
	}, {});

	for (const stagedFile of getSourceFiles()) {
		if (!tempLookup[stagedFile]) {
			const ffmpegCommand = FFMPEG_COMMAND.replace("$file", path.join(SOURCE, stagedFile + ".wav")).replace(
				"$outPath",
				path.join(OUTPUT, stagedFile + ".ogg")
			);
			console.log(`Running ${ffmpegCommand}`);
			await execCommand(ffmpegCommand);
		}
		// Lesson learnt: Do NOT delete the source...
		// fs.rmSync(path.join(STAGE, stagedFile + ".wav"));
	}

	console.log("All files committed");
}

async function main() {
	await doCommit();

	const tempLookup = getSourceFiles().reduce((acc, file) => {
		acc[file] = true;
		return acc;
	}, {});
	const wordToTTS = [];
	for (let word of words) {
		word = word.trim();
		if (word) {
			if (!tempLookup[word]) {
				wordToTTS.push(word);
			} else {
				console.log(`Skipping ${word} since it exists in either stage or out`);
			}
		}
	}

	for (const word of wordToTTS) {
		await runTTS(word);
	}
	await doCommit();
}

main();
