import { APIResponseSchemaNoData } from '@agentuity/server';
import { SessionStartEventDelayedSchema, SessionCompleteEventDelayedSchema, StructuredError, } from '@agentuity/core';
import { context, trace, SpanStatusCode } from '@opentelemetry/api';
import { internal } from '../../logger/internal';
const SessionResponseError = StructuredError('SessionResponseError');
/**
 * An implementation of the SessionEventProvider which uses HTTP for delivery.
 *
 * This provider checks that the event has required fields (orgId, projectId for start events)
 * before sending to the backend. If required fields are missing, the event is silently skipped.
 */
export class HTTPSessionEventProvider {
    apiClient;
    logger;
    constructor(client, logger) {
        this.apiClient = client;
        this.logger = logger;
    }
    /**
     * Check if a start event has all required fields for HTTP delivery
     */
    canSendStartEvent(event) {
        // orgId and projectId are required for the backend
        if (!event.orgId || !event.projectId) {
            internal.info('[session-http] skipping start event - missing required fields: orgId=%s, projectId=%s', event.orgId ?? 'missing', event.projectId ?? 'missing');
            return false;
        }
        return true;
    }
    /**
     * called when the session starts
     *
     * @param event SessionStartEvent
     */
    async start(event) {
        // Check required fields before sending
        if (!this.canSendStartEvent(event)) {
            return;
        }
        const tracer = trace.getTracer('session');
        const currentContext = context.active();
        const span = tracer.startSpan('Session Start', {}, currentContext);
        try {
            internal.info('[session-http] sending start event: %s', event.id);
            this.logger.debug('Sending session start event: %s', event.id);
            const spanContext = trace.setSpan(currentContext, span);
            const resp = await context.with(spanContext, () => this.apiClient.post('/session', { ...event, timestamp: Date.now() }, APIResponseSchemaNoData(), SessionStartEventDelayedSchema));
            if (resp.success) {
                internal.info('[session-http] start event sent successfully: %s', event.id);
                this.logger.debug('Session start event sent successfully: %s', event.id);
                this.startedSessions.add(event.id);
                span.setStatus({ code: SpanStatusCode.OK });
                return;
            }
            internal.info('[session-http] start event failed: %s - %s', event.id, resp.message);
            span.setStatus({ code: SpanStatusCode.ERROR, message: resp.message });
            throw new SessionResponseError({ message: resp.message });
        }
        catch (error) {
            span.recordException(error);
            span.setStatus({
                code: SpanStatusCode.ERROR,
                message: error instanceof Error ? error.message : String(error),
            });
            throw error;
        }
        finally {
            span.end();
        }
    }
    /**
     * Track session IDs that have been started (to know if we should send complete)
     */
    startedSessions = new Set();
    /**
     * called when the session completes
     *
     * @param event SessionCompleteEvent
     */
    async complete(event) {
        // Always create the "Session End" span for telemetry purposes.
        // This span is used by Catalyst to detect when a session has completed,
        // so it must always be emitted even if we don't send the HTTP event.
        const tracer = trace.getTracer('session');
        const currentContext = context.active();
        const span = tracer.startSpan('Session End', {}, currentContext);
        try {
            // Only send HTTP complete event if we successfully sent a start event.
            // This prevents sending orphaned complete events when start was skipped.
            // However, we still create the span above for telemetry.
            if (!this.startedSessions.has(event.id)) {
                internal.info('[session-http] skipping HTTP complete event (no matching start), but emitting Session End span: %s', event.id);
                span.setStatus({ code: SpanStatusCode.OK });
                return;
            }
            internal.info('[session-http] sending complete event: %s, userData: %s', event.id, event.userData ? `${event.userData.length} bytes` : 'none');
            this.logger.debug('Sending session complete event: %s', event.id);
            const spanContext = trace.setSpan(currentContext, span);
            const resp = await context.with(spanContext, () => this.apiClient.put('/session', { ...event, timestamp: Date.now() }, APIResponseSchemaNoData(), SessionCompleteEventDelayedSchema));
            if (resp.success) {
                this.startedSessions.delete(event.id);
                internal.info('[session-http] complete event sent successfully: %s', event.id);
                this.logger.debug('Session complete event sent successfully: %s', event.id);
                span.setStatus({ code: SpanStatusCode.OK });
                return;
            }
            internal.info('[session-http] complete event failed: %s - %s', event.id, resp.message);
            span.setStatus({ code: SpanStatusCode.ERROR, message: resp.message });
            throw new SessionResponseError({ message: resp.message });
        }
        catch (error) {
            span.recordException(error);
            span.setStatus({
                code: SpanStatusCode.ERROR,
                message: error instanceof Error ? error.message : String(error),
            });
            throw error;
        }
        finally {
            span.end();
        }
    }
}
//# sourceMappingURL=http.js.map