#!/usr/bin/env node
// @ts-check
/**
 * @file
 * Updates the SDK Typescript definitions located in the file sdk.d.ts
 * to match the current definitions of functions of sdk.mjs.
 */

import {spawnSync} from 'node:child_process';
import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import {fileExists} from '../utils.js';

const rootDirectory = path.resolve(import.meta.dirname, '..', '..');

const sdkTs = path.resolve(rootDirectory, 'sdk.d.ts');
const sdkMts = path.resolve(rootDirectory, 'sdk.d.mts');
const sdkMjs = path.resolve(rootDirectory, 'sdk.mjs');

const generateSdkMts = async () => {
	// Remove temporally type definitions imported with comments
	// in sdk.mjs to avoid circular imports
	const originalSdkMjsContent = await fs.readFile(sdkMjs, 'utf8');
	const temporarySdkMjsContent = originalSdkMjsContent
		.split('\n')
		.filter((line) => !line.startsWith(' * @typedef {import("./sdk")'))
		.join('\n');
	await fs.writeFile(sdkMjs, temporarySdkMjsContent);

	let cmd;
	let error = false;
	try {
		cmd = spawnSync(
			'npx',
			[
				'tsc',
				'-p',
				path.join('scripts', 'release', 'sdk-ts-defs-jsconfig.json'),
				'--declaration',
				'--emitDeclarationOnly',
			],
			{stdio: 'inherit'},
		);
	} catch {
		await fs.writeFile(sdkMjs, originalSdkMjsContent);
		error = true;
	}

	let errorMessage = "Command 'npx tsc sdk.mjs' failed for an unknown reason";
	if (cmd && cmd.stderr) {
		errorMessage = cmd.stderr.toString();
	}

	if (error || cmd === undefined) {
		process.stdout.write(
			`Error generating Typescript definitions: '${errorMessage}'\n`,
		);

		process.exit(1);
	}

	await fs.writeFile(sdkMjs, originalSdkMjsContent);
};

/**
 * We must remove the duplicated export types that tsc generates from
 * JSDoc `typedef` comments.
 * See {@link https://github.com/microsoft/TypeScript/issues/46011}.
 * @param {string} content Content of the file.
 * @returns {string} The content without duplicated export types.
 */
const removeDuplicatedExportTypes = (content) => {
	const newContent = [];
	const lines = content.split('\n');
	/** @type {string[]} */
	const exportTypesFound = [];

	for (const line of lines) {
		if (line.startsWith('export type ')) {
			const type = line.split(' ')[2];
			if (!exportTypesFound.includes(type)) {
				newContent.push(line);
				exportTypesFound.push(type);
			}
		} else {
			newContent.push(line);
		}
	}

	return newContent.join('\n');
};

const generateSdkTs = async () => {
	const sdkMtsExists = await fileExists(sdkMts);
	if (sdkMtsExists) await fs.unlink(sdkMts);
	await generateSdkMts();

	const autogeneratedMessage =
		'/* The next code is autogenerated from sdk.mjs */\n/* eslint-disable */';
	const newSdkTsContent =
		// eslint-disable-next-line unicorn/no-await-expression-member
		(await fs.readFile(sdkTs, 'utf8')).split(autogeneratedMessage)[0] +
		`${autogeneratedMessage}\n\n${await fs.readFile(sdkMts, 'utf8')}`;

	await fs.writeFile(sdkTs, removeDuplicatedExportTypes(newSdkTsContent));
	await fs.unlink(sdkMts);

	let cmd;
	let error = false;
	try {
		cmd = spawnSync('npx', ['prettier', '-w', 'sdk.d.ts'], {stdio: 'inherit'});
	} catch {
		error = true;
	}

	let errorMessage =
		"Command 'npx prettier -w sdk.d.ts' failed for an unknown reason";
	if (cmd && cmd.stderr) {
		errorMessage = cmd.stderr.toString();
	}

	if (error || cmd === undefined) {
		process.stdout.write(
			'Error executing Prettier to prettify' +
				` SDK TS definitions: '${errorMessage}'\n`,
		);
		process.exit(1);
	}
};

await generateSdkTs();
