#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run --allow-env
// vim:ft=typescript:ts=4:et
/*
 * Generates bin/rapydscript.mjs — the deno compilation entry point — by scanning
 * the filesystem for embeddable assets and prepending their declarations
 * and the __rapydscript_embedded__ global setup to the contents of bin/rapydscript.
 *
 * Usage:
 *   deno run --allow-read --allow-write bin/build.ts                         # generate bin/rapydscript.mjs only
 *   deno run --allow-read --allow-write --allow-run bin/build.ts --compile --output foo  # generate + deno compile
 *   deno run --allow-read --allow-write --allow-run bin/build.ts --compile --output foo --target x86_64-unknown-linux-gnu
 */

import { dirname, fromFileUrl, join, relative } from "jsr:@std/path";

const bin_dir = dirname(fromFileUrl(import.meta.url));
const repo_root = dirname(bin_dir);
const dev_dir = join(repo_root, 'dev');
const src_lib_dir = join(repo_root, 'src', 'lib');
const tools_dir = join(repo_root, 'tools');
const out_path = join(bin_dir, 'rapydscript.mjs');

async function exists(p: string): Promise<boolean> {
    try { await Deno.stat(p); return true; } catch { return false; }
}

// --- Scan assets ---

const DEV_TEXT_ASSETS = ['compiler.js', 'baselib-plain-pretty.js', 'baselib-plain-ugly.js'];
const DEV_JSON_ASSETS = ['stdlib_modules.json'];

const found_dev_text: string[] = [];
for (const a of DEV_TEXT_ASSETS) {
    if (await exists(join(dev_dir, a))) found_dev_text.push(a);
}

const found_dev_json: string[] = [];
for (const a of DEV_JSON_ASSETS) {
    if (await exists(join(dev_dir, a))) found_dev_json.push(a);
}

const stdlib_files: string[] = [];
for await (const entry of Deno.readDir(src_lib_dir)) {
    if (entry.isFile && entry.name.endsWith('.pyj')) {
        stdlib_files.push(entry.name);
    }
}
stdlib_files.sort();

// --- Build the generated asset section ---

function var_name(prefix: string, filename: string): string {
    return prefix + filename.replace(/[^a-zA-Z0-9]/g, '_');
}

const lines: string[] = [];

lines.push('// Generated by bin/build.ts — do not edit manually');
lines.push('// vim:ft=javascript:ts=4:et');
lines.push('');
lines.push('// === Embedded assets (generated by bin/build.ts) ===');
lines.push('// Assets are read at startup via Deno.readTextFileSync so they are embedded');
lines.push('// in compiled binaries when the corresponding files are passed to deno compile --include.');
lines.push('');

for (const a of found_dev_text) {
    lines.push(`const ${var_name('_dev_', a)} = Deno.readTextFileSync(new URL('../dev/${a}', import.meta.url));`);
}
for (const a of found_dev_json) {
    lines.push(`const ${var_name('_dev_', a)} = JSON.parse(Deno.readTextFileSync(new URL('../dev/${a}', import.meta.url)));`);
}
for (const f of stdlib_files) {
    lines.push(`const ${var_name('_stdlib_', f)} = Deno.readTextFileSync(new URL('../src/lib/${f}', import.meta.url));`);
}

lines.push('');
lines.push('globalThis.__rapydscript_embedded__ = {');

for (const a of found_dev_text) {
    lines.push(`    '${a}': ${var_name('_dev_', a)},`);
}
for (const a of found_dev_json) {
    // strip extension for the key so consumers use e.g. embedded.stdlib_modules
    const key = a.replace(/\.json$/, '').replace(/-/g, '_');
    lines.push(`    ${key}: ${var_name('_dev_', a)},`);
}
lines.push('    stdlib: {');
for (const f of stdlib_files) {
    lines.push(`        '${f}': ${var_name('_stdlib_', f)},`);
}
lines.push('    },');
// In the compiled binary the tools/ directory is embedded via --include, so the
// lint worker and all its transitive imports are accessible at this URL.
lines.push("    'lint-worker-url': new URL('../tools/lint-worker.mjs', import.meta.url),");
lines.push('};');
lines.push('');
lines.push('// === Entry point (from bin/rapydscript) ===');
lines.push('');

// --- Append bin/rapydscript content (strip the shebang line) ---

const entry_src = await Deno.readTextFile(join(bin_dir, 'rapydscript'));
const entry_lines = entry_src.split('\n');
// Drop the first line if it's the shebang, and the vim modeline comment right after
let start = 0;
if (entry_lines[0].startsWith('#!')) start = 1;
if (start < entry_lines.length && entry_lines[start].trim().startsWith('// vim:')) start++;

lines.push(...entry_lines.slice(start));

// Ensure the file ends with a newline
const content = lines.join('\n');
const final_content = content.endsWith('\n') ? content : content + '\n';

await Deno.writeTextFile(out_path, final_content);
console.log(`Written ${relative(repo_root, out_path)}`);
console.log(`  ${found_dev_text.length} dev text assets, ${found_dev_json.length} dev json assets, ${stdlib_files.length} stdlib files`);

// --- Optionally run deno compile with passed-through args ---

const extra_args = Deno.args.slice();
const compile_idx = extra_args.indexOf('--compile');
if (compile_idx !== -1) {
    extra_args.splice(compile_idx, 1);  // remove --compile; remaining args pass through

    // Build --include list: dev assets, stdlib .pyj files, and the entire tools/
    // directory so lint-worker.mjs and its transitive imports are available.
    const include_flags: string[] = [];
    for (const a of found_dev_text) include_flags.push('--include', join(dev_dir, a));
    for (const a of found_dev_json) include_flags.push('--include', join(dev_dir, a));
    for (const f of stdlib_files) include_flags.push('--include', join(src_lib_dir, f));
    include_flags.push('--include', tools_dir);

    const cmd_args = [
        'compile',
        '--allow-read', '--allow-write', '--allow-run', '--allow-env', '--allow-sys',
        ...include_flags,
        ...extra_args,  // e.g. --output foo --target x86_64-unknown-linux-gnu
        out_path,
    ];
    console.log(`Running: deno ${cmd_args.join(' ')}`);
    const proc = new Deno.Command('deno', {
        args: cmd_args,
        stdout: 'inherit',
        stderr: 'inherit',
        stdin: 'inherit',
        cwd: repo_root,
    });
    const result = await proc.output();
    Deno.exit(result.code);
}
