import { createDocApi, createBoundDocApi, } from './generated/client.js';
import { CONTRACT } from './generated/contract.js';
import { SuperDocRuntime, } from './runtime/process.js';
import { SuperDocCliError } from './runtime/errors.js';
// ---------------------------------------------------------------------------
// Session-bound runtime wrapper
// ---------------------------------------------------------------------------
/**
 * Wraps a raw runtime and injects a fixed sessionId into every invoke call.
 * Implements RuntimeInvoker so generated code can use it directly.
 *
 * @internal
 */
class BoundRuntime {
    runtime;
    sessionId;
    closed = false;
    constructor(runtime, sessionId) {
        this.runtime = runtime;
        this.sessionId = sessionId;
    }
    async invoke(operation, params = {}, options = {}) {
        if (this.closed) {
            throw new SuperDocCliError(`Document handle is closed; cannot invoke ${operation.operationId}.`, {
                code: 'DOCUMENT_CLOSED',
                details: { sessionId: this.sessionId, operationId: operation.operationId },
            });
        }
        return this.runtime.invoke(operation, { ...params, sessionId: this.sessionId }, options);
    }
    markClosed() {
        this.closed = true;
    }
}
// ---------------------------------------------------------------------------
// Document handle
// ---------------------------------------------------------------------------
/**
 * Bound document handle. All document operations are available as typed methods.
 * The handle injects its session id automatically — callers never pass
 * doc or sessionId.
 */
class SuperDocDocumentCore {
    boundRuntime;
    _sessionId;
    _openResult;
    client;
    /** @internal */
    constructor(boundRuntime, sessionId, openResult, client) {
        this.boundRuntime = boundRuntime;
        this._sessionId = sessionId;
        this._openResult = openResult;
        this.client = client;
        attachBoundDocApi(this, createBoundDocApi(this.boundRuntime));
    }
    get sessionId() {
        return this._sessionId;
    }
    /** Read-only snapshot of the initial doc.open response metadata. */
    get openResult() {
        return this._openResult;
    }
    async save(params = {}, options = {}) {
        return this.boundRuntime.invoke(CONTRACT.operations['doc.save'], params, options);
    }
    async close(params = {}, options = {}) {
        const result = await this.boundRuntime.invoke(CONTRACT.operations['doc.close'], params, options);
        this.boundRuntime.markClosed();
        this.client.removeHandle(this._sessionId);
        return result;
    }
    /** @internal */
    markClosed() {
        this.boundRuntime.markClosed();
    }
    async formatRange(params, options = {}) {
        const { properties, ...rest } = params;
        return this.boundRuntime.invoke(CONTRACT.operations['doc.format.apply'], {
            ...rest,
            inline: properties,
        }, options);
    }
}
function attachBoundDocApi(target, api) {
    const { save: _save, close: _close, ...boundMethods } = api;
    Object.assign(target, boundMethods);
}
export const SuperDocDocument = SuperDocDocumentCore;
/**
 * SuperDoc client — transport manager and document factory.
 *
 * Use `client.open()` to get bound document handles. Each handle is
 * independently session-scoped and safe for concurrent use.
 *
 *     const client = new SuperDocClient({ user: { name: 'bot' } });
 *     await client.connect();
 *     const doc = await client.open({ doc: './file.docx' });
 *     const markdown = await doc.getMarkdown();
 *     await doc.close();
 *     await client.dispose();
 */
export class SuperDocClient {
    runtime;
    rawApi;
    handles = new Map();
    constructor(options = {}) {
        this.runtime = new SuperDocRuntime(options);
        this.rawApi = createDocApi(this.runtime);
    }
    async connect() {
        await this.runtime.connect();
    }
    /**
     * Open a document and return a bound document handle.
     *
     * The returned handle injects its session id into every operation
     * automatically. The same file can be opened multiple times with
     * different session ids (useful for diff workflows).
     */
    async open(params, options) {
        const explicitSessionId = params.sessionId;
        if (typeof explicitSessionId === 'string' && this.handles.has(explicitSessionId)) {
            throw new SuperDocCliError(`Session id already open in this client: ${explicitSessionId}`, {
                code: 'SESSION_ALREADY_OPEN',
                details: { sessionId: explicitSessionId },
            });
        }
        const result = (await this.rawApi.open(params, options));
        const contextId = result.contextId;
        const boundRuntime = new BoundRuntime(this.runtime, contextId);
        const handle = new SuperDocDocument(boundRuntime, contextId, result, this);
        this.handles.set(contextId, handle);
        return handle;
    }
    async describe(params = {}, options) {
        return this.rawApi.describe(params, options);
    }
    async describeCommand(params, options) {
        return this.rawApi.describeCommand(params, options);
    }
    async dispose() {
        for (const handle of this.handles.values()) {
            handle.markClosed();
        }
        this.handles.clear();
        await this.runtime.dispose();
    }
    /** @internal */
    removeHandle(sessionId) {
        this.handles.delete(sessionId);
    }
}
export function createSuperDocClient(options = {}) {
    return new SuperDocClient(options);
}
export { getSkill, installSkill, listSkills } from './skills.js';
export { chooseTools, dispatchSuperDocTool, getMcpPrompt, getSystemPrompt, getSystemPromptForProvider, getToolCatalog, listTools, DEFAULT_PRESET, getPreset, listPresets, } from './tools.js';
export { dispatchIntentTool } from './generated/intent-dispatch.generated.js';
export { SuperDocCliError } from './runtime/errors.js';
