import { execCommand } from '@joplin/utils';
import { insertContentIntoFile, rootDir } from './tool-utils';
import { remove } from 'fs-extra';

const sqlts = require('@rmp135/sql-ts').default;
const fs = require('fs-extra');

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
function createRuntimeObject(table: any) {
	const colStrings = [];
	for (const col of table.columns) {
		const name = col.propertyName;
		const type = col.propertyType;
		colStrings.push(`\t\t${name}: { type: '${type}' },`);
	}

	return `\t${table.name}: {\n${colStrings.join('\n')}\n\t},`;
}

const stringToSingular = (word: string) => {
	if (word.endsWith('s')) return word.substring(0, word.length - 1);
	return word;
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const generateListRenderDependencyType = (tables: any[]) => {
	const output: string[] = [];

	for (const table of tables) {
		if (!['notes', 'folders'].includes(table.name)) continue;

		for (const col of table.columns) {
			const name = col.propertyName;
			output.push(`'${stringToSingular(table.name)}.${name}'`);
		}
	}

	return output.join(' | ');
};

async function main() {
	// Run the CLI app once so as to generate the database file
	process.chdir(`${rootDir}/packages/app-cli`);
	const profileDir = `${__dirname}/__generateTypesProfile`;
	await execCommand(['yarn', 'start', '--profile', profileDir, 'version']);

	try {
		const sqlTsConfig = {
			'client': 'sqlite3',
			'connection': {
				'filename': `${profileDir}/database.sqlite`,
			},
			'tableNameCasing': 'pascal',
			'singularTableNames': true,
			'useNullAsDefault': true, // To disable warning "sqlite does not support inserting default values"
			'excludedTables': [
				'main.notes_fts',
				'main.notes_fts_segments',
				'main.notes_fts_segdir',
				'main.notes_fts_docsize',
				'main.notes_fts_stat',
				'main.master_keys',
			],
		};

		const definitions = await sqlts.toObject(sqlTsConfig);

		// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
		definitions.tables = definitions.tables.map((t: any) => {
			t.columns.push({
				nullable: false,
				name: 'type_',
				type: 'int',
				optional: true,
				isEnum: false,
				propertyName: 'type_',
				propertyType: 'number',
			});

			return t;
		});

		// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
		definitions.tables = definitions.tables.map((table: any) => {
			// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
			table.columns = table.columns.map((column: any) => {
				return {
					...column,
					optional: true,
				};
			});

			return table;
		});

		const tableStrings = [];
		for (const table of definitions.tables) {
			tableStrings.push(createRuntimeObject(table));
		}

		const tsString = sqlts.fromObject(definitions, sqlTsConfig)
			.replace(/": /g, '"?: ');
		const header = `// AUTO-GENERATED BY ${__filename.substr(rootDir.length + 1)}`;

		const targetFile = `${rootDir}/packages/lib/services/database/types.ts`;
		console.info(`Writing type definitions to ${targetFile}...`);

		const existingContent = (await fs.pathExists(targetFile)) ? await fs.readFile(targetFile, 'utf8') : '';
		const splitted = existingContent.split('// AUTO-GENERATED BY');
		const staticContent = splitted[0];

		const runtimeContent = `export const databaseSchema: DatabaseTables = {\n${tableStrings.join('\n')}\n};`;

		const listRendererDependency = `type ListRendererDatabaseDependency = ${generateListRenderDependencyType(definitions.tables)};`;
		const noteListTypeFilePath = `${rootDir}/packages/lib/services/plugins/api/noteListType.ts`;

		await fs.writeFile(targetFile, `${staticContent.trim()}\n\n${header}\n\n${tsString}\n\n${runtimeContent}`, 'utf8');

		console.info(`Writing ListRendererDatabaseDependency type to ${noteListTypeFilePath}...`);

		await insertContentIntoFile(
			noteListTypeFilePath,
			'// AUTO-GENERATED by generate-database-type\n',
			'\n// AUTO-GENERATED by generate-database-type',
			listRendererDependency,
		);
	} finally {
		await remove(profileDir);
	}
}

main().catch((error) => {
	console.error(error);
	process.exit(1);
});
