import {cpSync} from 'node:fs';
import {tmpdir} from 'node:os';
import path from 'path';
import {$} from 'bun';
import {CreateVideoInternals} from 'create-video';

type MinimalTemplate = {
	shortName: string;
	org: string;
	repoName: string;
	defaultBranch: string;
	templateInMonorepo: string;
};

const folders = CreateVideoInternals.FEATURED_TEMPLATES.filter(
	(t) => t.templateInMonorepo !== null,
);

const skillsTemplate: MinimalTemplate = {
	defaultBranch: 'main',
	templateInMonorepo: 'skills',
	org: 'remotion-dev',
	repoName: 'skills',
	shortName: 'Skills',
};

const templates = [skillsTemplate, ...folders];

const publish = async (template: MinimalTemplate) => {
	const folder = path.join(
		__dirname,
		'..',
		'..',
		'..',
		template.templateInMonorepo as string,
	);

	const tmpDir = tmpdir();
	const workingDir = path.join(tmpDir, `template-${Math.random()}`);

	await $`git add .`.cwd(folder);
	const files = await $`git ls-files`.cwd(folder).quiet();
	const filesInTemplate = files.stdout
		.toString('utf-8')
		.trim()
		.split('\n')
		.filter(Boolean);

	await $`git clone git@github.com:${template.org}/${template.repoName}.git ${workingDir} --depth 1`;

	const defaultBranch = await $`git branch --show-current`
		.cwd(workingDir)
		.text();
	const existingFilesInRepo = await $`git ls-files`.cwd(workingDir).quiet();
	for (const file of existingFilesInRepo.stdout
		.toString('utf-8')
		.trim()
		.split('\n')) {
		if (file === '') continue;
		await $`rm ${file}`.cwd(workingDir).quiet();
	}

	for (const file of filesInTemplate) {
		const src = path.join(folder, file);
		const dst = path.join(workingDir, file);
		cpSync(src, dst);

		if (file === 'package.json') {
			const dstFile = await Bun.file(dst).text();
			const currentVersion = dstFile.replaceAll('workspace:*', '^4.0.0');
			await Bun.write(dst, currentVersion);
		}
	}

	await $`git add .`.cwd(workingDir).nothrow();
	const hasChanges = await $`git status --porcelain`.cwd(workingDir).text();
	if (!hasChanges) {
		console.log(`No changes in ${template.shortName}`);
		return;
	}

	await $`git commit -m "Update template"`.cwd(workingDir);
	await $`git push origin ${defaultBranch.trim()}`.cwd(workingDir);
};

const CONCURRENCY = 1;

const results: PromiseSettledResult<void>[] = [];

for (let i = 0; i < templates.length; i += CONCURRENCY) {
	const batch = templates.slice(i, i + CONCURRENCY);
	const batchResults = await Promise.allSettled(
		batch.map((template) => publish(template)),
	);
	results.push(...batchResults);
}

const failures = results.filter(
	(r): r is PromiseRejectedResult => r.status === 'rejected',
);

if (failures.length > 0) {
	console.error(`${failures.length} template(s) failed to publish:`);
	for (const failure of failures) {
		console.error(failure.reason);
	}

	process.exit(1);
}
