import { spawn } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import inquirer from 'inquirer';
import { runPostWriteValidation } from '../utils/post-write-validator.js';
const NAME = /^[a-zA-Z0-9][a-zA-Z0-9 _-]{0,63}$/;
const manager = (root, requested) => {
    if (requested)
        return requested;
    try {
        const value = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).packageManager?.split('@')[0];
        if (['npm', 'pnpm', 'yarn', 'bun'].includes(value))
            return value;
    }
    catch { }
    if (fs.existsSync(path.join(root, 'pnpm-lock.yaml')))
        return 'pnpm';
    if (fs.existsSync(path.join(root, 'yarn.lock')))
        return 'yarn';
    if (fs.existsSync(path.join(root, 'bun.lock')) || fs.existsSync(path.join(root, 'bun.lockb')))
        return 'bun';
    return 'npm';
};
const run = (pm, args, cwd) => new Promise((resolve, reject) => { const child = spawn(process.platform === 'win32' ? `${pm}.cmd` : pm, args, { cwd, stdio: 'inherit', windowsHide: true }); child.once('error', reject); child.once('exit', (code, signal) => code === 0 ? resolve() : reject(new Error(`${pm} exited with ${signal || code}`))); });
const html = `<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>VGE Game</title></head><body><canvas id="game"></canvas><div id="hud"><strong>Vigthoria Gaming Engine</strong><span>Ready</span></div><script type="module" src="/src/main.ts"></script></body></html>\n`;
const style = `*{box-sizing:border-box}html,body,#game{width:100%;height:100%;margin:0;overflow:hidden}body{background:#030510;color:#e8f7ff;font-family:Inter,system-ui,sans-serif}#game{display:block;touch-action:none}#hud{position:fixed;left:24px;top:24px;display:flex;gap:16px;padding:12px 16px;border:1px solid #00d4ff66;border-radius:12px;background:#070a18cc;backdrop-filter:blur(12px);box-shadow:0 0 32px #7c3aed33}#hud span{color:#5eead4}\n`;
const main = `import { ArcRotateCamera, Color3, Color4, DirectionalLight, Engine, HemisphericLight, MeshBuilder, PBRMaterial, Scene, ShadowGenerator, Vector3 } from '@babylonjs/core';
import '@babylonjs/loaders'; import './style.css';
const canvas=document.querySelector<HTMLCanvasElement>('#game')!; const engine=new Engine(canvas,true,{stencil:true}); const scene=new Scene(engine); scene.clearColor=new Color4(.012,.018,.05,1);
const camera=new ArcRotateCamera('camera',-Math.PI/2,Math.PI/3,12,new Vector3(0,1,0),scene); camera.attachControl(canvas,true); new HemisphericLight('sky',new Vector3(0,1,0),scene).intensity=.55;
const sun=new DirectionalLight('sun',new Vector3(-.5,-1,-.4),scene); sun.position=new Vector3(8,14,8); sun.intensity=2; const shadows=new ShadowGenerator(2048,sun); shadows.useBlurExponentialShadowMap=true;
const ground=MeshBuilder.CreateGround('ground',{width:30,height:30},scene); const gm=new PBRMaterial('ground-pbr',scene); gm.albedoColor=new Color3(.025,.06,.12); gm.metallic=.35; gm.roughness=.72; ground.material=gm; ground.receiveShadows=true;
const hero=MeshBuilder.CreateCapsule('hero',{height:2.4,radius:.55,tessellation:32},scene); hero.position.y=1.2; const hm=new PBRMaterial('hero-pbr',scene); hm.albedoColor=Color3.FromHexString('#7c3aed'); hm.metallic=.55; hm.roughness=.22; hero.material=hm; shadows.addShadowCaster(hero);
declare global { interface Window { __vgeDiagnostics: Record<string,unknown> } }
engine.runRenderLoop(()=>{hero.rotation.y+=engine.getDeltaTime()*.00035; scene.render(); window.__vgeDiagnostics={engineReady:true,sceneReady:true,activeCamera:camera.name,fps:Math.round(engine.getFps()),frameCount:Number(window.__vgeDiagnostics?.frameCount||0)+1,meshCount:scene.meshes.length,materialCount:scene.materials.length,assetErrors:[],consoleErrors:[]};}); addEventListener('resize',()=>engine.resize());\n`;
export class GameCommand {
    logger;
    constructor(logger) {
        this.logger = logger;
    }
    async init(input, o) {
        if (String(o.engine || 'babylon').toLowerCase() !== 'babylon')
            throw new Error('Unsupported engine. Use babylon.');
        let name = input?.trim();
        if (!name)
            name = (await inquirer.prompt([{ type: 'input', name: 'name', message: 'Game name:', validate: v => NAME.test(String(v).trim()) || 'Use 1-64 letters, numbers, spaces, dashes, or underscores.' }])).name.trim();
        if (!NAME.test(name))
            throw new Error('Invalid game name.');
        const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
        const root = path.resolve(process.cwd(), slug);
        if (fs.existsSync(root) && fs.readdirSync(root).length)
            throw new Error(`Target directory is not empty: ${root}`);
        fs.mkdirSync(path.join(root, 'src'), { recursive: true });
        const pkg = { name: slug, version: '0.1.0', private: true, type: 'module', vigthoria: { kind: 'game', engine: 'babylon', renderTransport: 'client-state', schemaVersion: 1 }, scripts: { dev: 'vite', build: 'tsc --noEmit && vite build', preview: 'vite preview' }, dependencies: { '@babylonjs/core': '7.54.3', '@babylonjs/loaders': '7.54.3' }, devDependencies: { typescript: '5.8.3', vite: '6.1.0' } };
        fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
        fs.writeFileSync(path.join(root, 'index.html'), html);
        fs.writeFileSync(path.join(root, 'src/main.ts'), main);
        fs.writeFileSync(path.join(root, 'src/style.css'), style);
        fs.writeFileSync(path.join(root, '.gitignore'), 'node_modules\ndist\n.vigthoria\n');
        fs.writeFileSync(path.join(root, 'tsconfig.json'), JSON.stringify({ compilerOptions: { target: 'ES2022', module: 'ESNext', lib: ['ES2022', 'DOM'], skipLibCheck: true, moduleResolution: 'Bundler', isolatedModules: true, noEmit: true, strict: true }, include: ['src'] }, null, 2) + '\n');
        if (o.install !== false)
            await run(manager(root, o.packageManager), ['install'], root);
        this.logger.success(`Babylon/VGE game created: ${root}`);
        this.logger.info(`Next: cd ${slug} && vigthoria game run`);
    }
    async run(o) { const root = path.resolve(o.project || process.cwd()); this.project(root); const args = ['run', 'dev', '--', '--host', '127.0.0.1']; if (o.port)
        args.push('--port', String(o.port)); if (o.open !== false)
        args.push('--open'); await run(manager(root, o.packageManager), args, root); }
    async validate(o) {
        const root = path.resolve(o.project || process.cwd());
        const checks = [];
        let pkg;
        try {
            pkg = this.project(root);
            checks.push({ check: 'package.json', passed: true });
        }
        catch (e) {
            checks.push({ check: 'package.json', passed: false, detail: e.message });
            return this.finish(checks, o.json);
        }
        const source = this.sources(root);
        checks.push({ check: 'Babylon client-state metadata', passed: pkg.vigthoria?.engine === 'babylon' && pkg.vigthoria?.renderTransport === 'client-state' }, { check: 'Pinned local Babylon dependencies', passed: /^\d/.test(pkg.dependencies?.['@babylonjs/core'] || '') && /^\d/.test(pkg.dependencies?.['@babylonjs/loaders'] || '') }, { check: 'No CDN engine imports', passed: !/https?:\/\/(cdn|unpkg|jsdelivr)[^\s"']*(babylon|three)/i.test(source) }, { check: 'Runtime diagnostics', passed: source.includes('__vgeDiagnostics') }, { check: 'Client source entry', passed: fs.existsSync(path.join(root, 'src/main.ts')) || fs.existsSync(path.join(root, 'src/main.js')) });
        try {
            await run(manager(root, o.packageManager), ['run', 'build'], root);
            checks.push({ check: 'Production build', passed: true });
        }
        catch (e) {
            checks.push({ check: 'Production build', passed: false, detail: e.message });
        }
        for (const result of await runPostWriteValidation(root))
            checks.push({ check: result.tool, passed: result.passed, detail: result.output || undefined });
        return this.finish(checks, o.json);
    }
    project(root) { if (!fs.existsSync(root) || !fs.statSync(root).isDirectory())
        throw new Error(`Project directory not found: ${root}`); const file = path.join(root, 'package.json'); if (!fs.existsSync(file))
        throw new Error('package.json not found'); const pkg = JSON.parse(fs.readFileSync(file, 'utf8')); if (!pkg.scripts?.dev || !pkg.scripts?.build)
        throw new Error('Game requires dev and build scripts'); return pkg; }
    sources(root) { const files = []; const walk = (d) => { for (const e of fs.readdirSync(d, { withFileTypes: true })) {
        if (['node_modules', 'dist', '.git'].includes(e.name))
            continue;
        const f = path.join(d, e.name);
        if (e.isDirectory())
            walk(f);
        else if (/\.(html|[cm]?[jt]sx?)$/.test(e.name))
            files.push(f);
    } }; walk(root); return files.map(f => fs.readFileSync(f, 'utf8')).join('\n'); }
    finish(checks, json = false) { const passed = checks.every(x => x.passed); if (json)
        console.log(JSON.stringify({ passed, checks }, null, 2));
    else
        for (const c of checks)
            (c.passed ? this.logger.success.bind(this.logger) : this.logger.error.bind(this.logger))(`${c.check}${c.detail ? `: ${c.detail.trim().slice(0, 500)}` : ''}`); if (!passed)
        process.exitCode = 1; return passed; }
}
