#!/usr/bin/env node
"use strict";var f=require("child_process"),g=require("fs"),N=require("path");function _(e){return e.replace(/[A-Z]/g,r=>`_${r.toLowerCase()}`)}function j(e){let r=JSON.parse(e);return{functions:r.functions.map(n=>({jsName:n.js_name,rustName:n.rust_name,paramNames:n.param_names,params:n.params,ret:n.ret,isAsync:n.is_async})),classes:(r.classes??[]).map(n=>({name:n.name,methods:n.methods.map(a=>({jsName:a.js_name,rustName:a.rust_name,paramNames:a.param_names,params:a.params,ret:a.ret,isAsync:a.is_async,isGetter:a.is_getter}))})),objects:(r.objects??[]).map(n=>({name:n.name,fieldNames:n.field_names,fieldTypes:n.field_types})),constants:(r.constants??[]).map(n=>({jsName:n.js_name,rustName:n.rust_name,tsType:n.ts_type,value:n.value}))}}function u(e){return e.params.map((r,n)=>{let a=e.paramNames[n]??`arg${n}`,i=r.endsWith("| undefined | null");return`${a}${i?"?":""}: ${r}`}).join(", ")}function b(e){return e.isAsync?`Promise<${e.ret}>`:e.ret}function v(e){return`[${e.functions.filter(n=>n.isAsync).map(n=>`'${n.jsName}'`).join(", ")}]`}function h(e){return`[${e.functions.filter(n=>n.ret==="ExternalObject").map(n=>`'${n.jsName}'`).join(", ")}]`}function S(e){return`{ ${e.functions.filter(n=>_(n.jsName)!==n.rustName).map(n=>`'${n.jsName}': '${n.rustName}'`).join(", ")} }`}function y(e,r="Bindings"){let n=new Set(e.classes.map(t=>t.name)),a=e.functions.filter(t=>n.has(t.ret)),i=e.objects.map(x).join(`

`),o=(e.constants??[]).map(t=>`export const ${t.jsName}: ${t.tsType} = ${JSON.stringify(t.value)};`).join(`
`),d=e.functions.map(t=>`  ${t.jsName}(${u(t)}): ${b(t)};`).join(`
`),s=e.classes.map(t=>w(t,e)).join(`

`),m=e.classes.map(t=>`  ${t.name}: typeof ${t.name};`).join(`
`),c=e.classes.length?`{ ${e.classes.map(t=>t.name).join(", ")} } as unknown as Record<string, new (...a: unknown[]) => unknown>`:"{}",$=a.length?`{ ${a.map(t=>`${t.jsName}: { cls: ${t.ret}, isAsync: ${t.isAsync}, rustName: '${t.rustName}' }`).join(", ")} } as unknown as Record<string, { cls: { __fromHandle(p: SyncProvider, h: unknown): unknown }; isAsync: boolean; rustName: string }>`:"{}";return`// Generated from the Rust #[napi] manifest. Do not edit.
import {
  createSyncBinding,
  bindClasses,
  type ExternalObject,
  type SyncProvider,
} from 'napi-oop-runtime';

export type { ExternalObject };

${i}${i?`

`:""}${o}${o?`

`:""}export interface ${r} {
${d}
${m}
}

${s}

const asyncFns: string[] = ${v(e)};
const externalFns: string[] = ${h(e)};
const wireNames: Record<string, string> = ${S(e)};

export const bind = (provider: SyncProvider): ${r} =>
  bindClasses(
    createSyncBinding<${r}>(provider, asyncFns, externalFns, wireNames),
    provider,
    ${c},
    ${$}
  );
`}function x(e){let r=e.fieldNames.map((n,a)=>`  ${n}: ${e.fieldTypes[a]??"unknown"};`).join(`
`);return`export interface ${e.name} {
${r}
}`}function w(e,r){let n=new Set(r.classes.map(s=>s.name)),a=e.methods.find(s=>s.jsName==="constructor"),i=a?u(a):"",o=a?a.paramNames.join(", "):"",d=e.methods.filter(s=>s.jsName!=="constructor").map(s=>{let m=n.has(s.ret),c=s.isAsync?`Promise<${s.ret}>`:s.ret,$=s.paramNames.length?", "+s.paramNames.join(", "):"",p=`this.__provider.${s.isAsync?"callAsync":"call"}('${s.rustName}', [this.__handle${$}])`,l=s.isAsync?m?`const r = (await ${p}) as ExternalObject; return ${s.ret}.__fromHandle(this.__provider, r);`:`return (await ${p}) as ${s.ret};`:m?`const r = ${p} as ExternalObject; return ${s.ret}.__fromHandle(this.__provider, r);`:`return ${p} as ${s.ret};`;return s.isGetter?s.isAsync?`  get ${s.jsName}(): ${c} { return (async () => { ${l} })(); }`:`  get ${s.jsName}(): ${c} { ${l} }`:s.isAsync?`  async ${s.jsName}(${u(s)}): ${c} { ${l} }`:`  ${s.jsName}(${u(s)}): ${c} { ${l} }`}).join(`
`);return`export class ${e.name} {
  private __provider: SyncProvider;
  private __handle: ExternalObject;
  constructor(${i}${a&&o?", ":""}__provider?: SyncProvider) {
    this.__provider = __provider as SyncProvider;
    this.__handle = this.__provider.call('${a?.rustName??e.name+".constructor"}', [${o}]) as ExternalObject;
    this.__provider.trackExternal(this.__handle);
  }
  static __fromHandle(provider: SyncProvider, handle: ExternalObject): ${e.name} {
    const o = Object.create(${e.name}.prototype) as ${e.name};
    (o as unknown as { __provider: SyncProvider }).__provider = provider;
    (o as unknown as { __handle: ExternalObject }).__handle = handle;
    provider.trackExternal(handle);
    return o;
  }
${d}
}`}function k(e){let[r,n,a="Bindings"]=e;(!r||!n)&&(console.error("usage: napi-oop-codegen <provider-binary> <out-dir> [InterfaceName]"),process.exit(2));let i=(0,f.execFileSync)(r,["--emit-manifest"],{encoding:"utf8"}),o=j(i);(0,g.mkdirSync)(n,{recursive:!0}),(0,g.writeFileSync)((0,N.join)(n,"bindings.ts"),y(o,a)),console.error(`generated ${o.functions.length} binding(s) -> ${n}/bindings.ts`)}k(process.argv.slice(2));
