import { ConversionMetaBag } from './meta/meta-bag.js';
export class PipelineValidationError extends Error {
    stage;
    errors;
    constructor(stage, errors) {
        super(errors[0]?.message ?? `Validation failed during ${stage}`);
        this.name = 'PipelineValidationError';
        this.stage = stage;
        this.errors = errors;
    }
}
function normalizeContext(hooks, ctx) {
    return {
        profileId: ctx?.profileId ?? hooks.id,
        providerProtocol: ctx?.providerProtocol ?? hooks.protocol,
        targetProtocol: ctx?.targetProtocol ?? ctx?.providerProtocol ?? hooks.protocol,
        entryEndpoint: ctx?.entryEndpoint,
        requestId: ctx?.requestId,
        stream: ctx?.stream,
        metadata: ctx?.metadata ?? {}
    };
}
function ingestMeta(target, incoming) {
    if (!incoming) {
        return;
    }
    target.ingest(incoming);
}
export class ProtocolConversionPipeline {
    hooks;
    constructor(hooks) {
        if (!hooks?.inbound?.parse) {
            throw new Error('ProtocolConversionPipeline requires an inbound.parse hook');
        }
        if (!hooks?.outbound?.serialize) {
            throw new Error('ProtocolConversionPipeline requires an outbound.serialize hook');
        }
        this.hooks = hooks;
    }
    async convertInbound(options) {
        const context = normalizeContext(this.hooks, options.context);
        const meta = new ConversionMetaBag(options.meta);
        await this.runValidation('inbound.preValidate', this.hooks.inbound.preValidate, {
            wire: options.payload,
            meta,
            context
        });
        const parseResult = await this.hooks.inbound.parse({
            wire: options.payload,
            meta,
            context
        });
        ingestMeta(meta, parseResult.meta);
        const canonical = parseResult.canonical;
        if (!canonical) {
            throw new Error('ProtocolConversionPipeline inbound.parse returned an empty canonical payload');
        }
        if (this.hooks.inbound.cleanup) {
            await this.hooks.inbound.cleanup({
                canonical,
                meta,
                context
            });
        }
        return { canonical, meta, context };
    }
    async convertOutbound(options) {
        const context = normalizeContext(this.hooks, options.context);
        const meta = new ConversionMetaBag(options.meta);
        if (this.hooks.outbound.augment) {
            await this.hooks.outbound.augment({
                canonical: options.canonical,
                meta,
                context
            });
        }
        const serialized = await this.hooks.outbound.serialize({
            canonical: options.canonical,
            meta,
            context
        });
        ingestMeta(meta, serialized.meta);
        await this.runValidation('outbound.postValidate', this.hooks.outbound.postValidate, {
            wire: serialized.payload,
            meta,
            context
        });
        return { payload: serialized.payload, meta, context };
    }
    async runValidation(stage, hook, args) {
        if (!hook) {
            return;
        }
        const result = await hook(args);
        if (!result) {
            return;
        }
        const errors = result.errors ?? [];
        if (result.ok === false || errors.length > 0) {
            throw new PipelineValidationError(stage, errors.length ? errors : [{ message: 'Unknown validation error' }]);
        }
    }
}
