import { z } from 'zod';
import { BaseConnector } from '../BaseConnector.js';
const HttpConnectorConfigSchema = z.object({
    baseUrl: z.string().url(),
    defaultHeaders: z.record(z.string()).optional(),
    timeoutMs: z.number().positive().optional().default(30_000),
});
const HttpRequestInputSchema = z.object({
    method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD']),
    path: z.string(),
    headers: z.record(z.string()).optional(),
    query: z.record(z.string()).optional(),
    body: z.unknown().optional(),
});
export class HttpConnector extends BaseConnector {
    id = 'conn_http';
    name = 'HTTP Connector';
    description = 'Generic HTTP/REST API connector for making HTTP requests';
    connectionType = 'http';
    configSchema = HttpConnectorConfigSchema;
    methods = [
        {
            name: 'request',
            description: 'Make an HTTP request to the configured base URL',
            inputSchema: HttpRequestInputSchema,
        },
    ];
    baseUrl = '';
    defaultHeaders = {};
    timeoutMs = 30_000;
    async connect(config, auth) {
        this.config = config;
        this.auth = auth;
        this.baseUrl = config.baseUrl.replace(/\/+$/, '');
        this.defaultHeaders = config.defaultHeaders ?? {};
        this.timeoutMs = config.timeoutMs ?? 30_000;
        if (auth) {
            Object.assign(this.defaultHeaders, this.resolveAuthHeaders(auth));
        }
        this.log.info(`HTTP connector connected to ${this.baseUrl}`);
    }
    async disconnect() {
        this.config = null;
        this.auth = undefined;
        this.baseUrl = '';
        this.defaultHeaders = {};
        this.log.info('HTTP connector disconnected');
    }
    async healthCheck() {
        if (!this.baseUrl)
            return false;
        try {
            const controller = new AbortController();
            const timeout = setTimeout(() => controller.abort(), 5_000);
            const response = await fetch(this.baseUrl, {
                method: 'HEAD',
                signal: controller.signal,
            });
            clearTimeout(timeout);
            return response.ok || response.status < 500;
        }
        catch {
            return false;
        }
    }
    async execute(method, input) {
        this.requireMethod(method);
        const validated = this.validateInput(this.requireMethod(method), input);
        const { result, durationMs } = await this.measureExecution(() => this.doRequest(validated));
        return {
            success: result.status >= 200 && result.status < 400,
            output: result,
            durationMs,
            metadata: {
                status: result.status,
                statusText: result.statusText,
            },
        };
    }
    async doRequest(input) {
        const url = new URL(input.path, `${this.baseUrl}/`);
        if (input.query) {
            for (const [key, value] of Object.entries(input.query)) {
                url.searchParams.set(key, value);
            }
        }
        const headers = {
            ...this.defaultHeaders,
            ...input.headers,
        };
        if (input.body && !headers['content-type'] && !headers['Content-Type']) {
            headers['Content-Type'] = 'application/json';
        }
        const controller = new AbortController();
        const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
        try {
            const response = await fetch(url.toString(), {
                method: input.method,
                headers,
                body: input.body !== undefined ? JSON.stringify(input.body) : undefined,
                signal: controller.signal,
            });
            const responseHeaders = {};
            response.headers.forEach((value, key) => {
                responseHeaders[key] = value;
            });
            let body;
            const contentType = response.headers.get('content-type') ?? '';
            if (contentType.includes('application/json')) {
                body = await response.json();
            }
            else {
                body = await response.text();
            }
            return {
                status: response.status,
                statusText: response.statusText,
                headers: responseHeaders,
                body,
            };
        }
        finally {
            clearTimeout(timeout);
        }
    }
    resolveAuthHeaders(auth) {
        const creds = auth.credentials ?? {};
        switch (auth.type) {
            case 'api_key': {
                const apiKey = creds.apiKey;
                if (!apiKey)
                    throw new Error('AuthConfig api_key: missing required credential "apiKey"');
                return {
                    [creds.headerName ?? 'X-API-Key']: apiKey,
                };
            }
            case 'bearer': {
                const token = creds.token;
                if (!token)
                    throw new Error('AuthConfig bearer: missing required credential "token"');
                return { Authorization: `Bearer ${token}` };
            }
            case 'basic': {
                const username = creds.username;
                const password = creds.password;
                if (!username || !password)
                    throw new Error('AuthConfig basic: missing required credentials "username" and "password"');
                const encoded = btoa(`${username}:${password}`);
                return { Authorization: `Basic ${encoded}` };
            }
            case 'none':
            case 'oauth2':
            case 'custom':
                return {};
            default: {
                const _exhaustive = auth.type;
                throw new Error(`Unhandled auth type: ${_exhaustive}`);
            }
        }
    }
}
//# sourceMappingURL=http.js.map