// SPDX-License-Identifier: AGPL-3.0-or-later

import {existsSync, readFileSync, rmSync, statSync} from 'node:fs';
import {isBuiltin} from 'node:module';
import {dirname, join, resolve} from 'node:path';
import {fileURLToPath} from 'node:url';
import {build} from 'esbuild';

const API_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const REPO_ROOT = resolve(API_ROOT, '..');
const OUT_DIR = join(API_ROOT, 'dist');
const CANDIDATE_SUFFIXES = ['', '.ts', '.tsx', '.js', '.mjs', '/index.ts', '/index.tsx', '/index.js', '/src/index.ts'];

const WORKSPACE_ROOTS = {
	'@app/': join(API_ROOT, 'src'),
	'@pkgs/': join(API_ROOT, 'pkgs'),
	'@fluxer/': join(REPO_ROOT, 'packages'),
};

function resolveWorkspacePath(specifier) {
	for (const [prefix, root] of Object.entries(WORKSPACE_ROOTS)) {
		if (!specifier.startsWith(prefix)) {
			continue;
		}
		const base = join(root, specifier.slice(prefix.length));
		for (const suffix of CANDIDATE_SUFFIXES) {
			const candidate = `${base}${suffix}`;
			if (existsSync(candidate) && statSync(candidate).isFile()) {
				return candidate;
			}
		}
		throw new Error(`Unable to resolve workspace import ${specifier}`);
	}
	return null;
}

function packageNameOf(specifier) {
	const segments = specifier.split('/');
	return specifier.startsWith('@') ? segments.slice(0, 2).join('/') : segments[0];
}

const declaredDependencies = new Set(
	Object.keys(JSON.parse(readFileSync(join(API_ROOT, 'package.json'), 'utf-8')).dependencies),
);
const undeclaredDependencies = new Set();

const workspacePlugin = {
	name: 'fluxer-workspace',
	setup(pluginBuild) {
		pluginBuild.onResolve({filter: /^[^./]/}, (args) => {
			const workspacePath = resolveWorkspacePath(args.path);
			if (workspacePath) {
				return {path: workspacePath};
			}
			const packageName = packageNameOf(args.path);
			if (!isBuiltin(args.path) && !declaredDependencies.has(packageName)) {
				undeclaredDependencies.add(packageName);
			}
			return {path: args.path, external: true};
		});
	},
};

rmSync(OUT_DIR, {recursive: true, force: true});

await build({
	entryPoints: [join(API_ROOT, 'src/AppEntrypoint.ts'), join(API_ROOT, 'src/WorkerEntrypoint.ts')],
	outdir: OUT_DIR,
	bundle: true,
	platform: 'node',
	format: 'esm',
	target: 'node24',
	charset: 'utf8',
	sourcemap: true,
	sourcesContent: false,
	logLevel: 'info',
	banner: {js: '// SPDX-License-Identifier: AGPL-3.0-or-later'},
	plugins: [workspacePlugin],
});

if (undeclaredDependencies.size > 0) {
	const names = [...undeclaredDependencies].sort().join(', ');
	throw new Error(
		`The bundle imports packages that fluxer_api does not declare as dependencies: ${names}. ` +
			'Workspace packages keep their own node_modules, so the bundle cannot reach them from fluxer_api.',
	);
}
