/**
 * Registry HTTP Client
 *
 * Fetches instance factory metadata from the SpecVerse Registry API.
 * Uses Node.js 18+ built-in fetch with retry logic and timeout handling.
 *
 * @module registry/client/registry-client
 * @version 2.0.0
 */
/**
 * Default configuration
 */
const DEFAULT_CONFIG = {
    endpoint: process.env.SPECVERSE_REGISTRY_URL || 'https://specverse-lang-registry-api.vercel.app',
    timeout: 10000,
    retries: 3,
    userAgent: 'SpecVerse CLI'
};
/**
 * Registry HTTP Client
 *
 * Handles communication with the SpecVerse Registry API to fetch
 * instance factory metadata.
 */
export class RegistryClient {
    config;
    constructor(config) {
        this.config = { ...DEFAULT_CONFIG, ...config };
    }
    /**
     * Fetch all instance factory metadata
     *
     * @param options - Query options for filtering
     * @returns Registry response with factory metadata
     */
    async fetchFactories(options = {}) {
        const params = new URLSearchParams({ type: 'factory' });
        if (options.capability) {
            params.append('capability', options.capability);
        }
        if (options.framework) {
            params.append('framework', options.framework);
        }
        if (options.tags && options.tags.length > 0) {
            params.append('tags', options.tags.join(','));
        }
        if (options.deprecated !== undefined) {
            params.append('deprecated', String(options.deprecated));
        }
        if (options.page !== undefined) {
            params.append('page', String(options.page));
        }
        if (options.pageSize !== undefined) {
            params.append('pageSize', String(options.pageSize));
        }
        const url = `${this.config.endpoint}/api/libraries?${params}`;
        try {
            const response = await this.fetchWithRetry(url);
            if (!response.ok) {
                throw new Error(`Registry returned ${response.status}: ${response.statusText}`);
            }
            const data = await response.json();
            return this.transformResponse(data);
        }
        catch (error) {
            throw new RegistryFetchError('Failed to fetch registry metadata', error);
        }
    }
    /**
     * Get specific factory metadata by name
     *
     * @param name - Factory name (e.g., "PrismaPostgres")
     * @returns Factory metadata or null if not found
     */
    async getFactory(name) {
        const url = `${this.config.endpoint}/api/factories/${encodeURIComponent(name)}`;
        try {
            const response = await this.fetchWithRetry(url);
            if (response.status === 404) {
                return null;
            }
            if (!response.ok) {
                throw new Error(`Registry returned ${response.status}: ${response.statusText}`);
            }
            const data = await response.json();
            return this.transformFactory(data.factory || data);
        }
        catch (error) {
            if (error instanceof RegistryFetchError) {
                throw error;
            }
            throw new RegistryFetchError(`Failed to fetch factory "${name}"`, error);
        }
    }
    /**
     * Search factories with fuzzy matching
     *
     * @param query - Search query string
     * @param options - Search options
     * @returns Search results with scores
     */
    async search(query, options = {}) {
        const params = new URLSearchParams({ q: query, type: 'factory' });
        if (options.capability) {
            params.append('capability', options.capability);
        }
        if (options.framework) {
            params.append('framework', options.framework);
        }
        if (options.tags && options.tags.length > 0) {
            params.append('tags', options.tags.join(','));
        }
        const url = `${this.config.endpoint}/api/search?${params}`;
        try {
            const response = await this.fetchWithRetry(url);
            if (!response.ok) {
                throw new Error(`Registry returned ${response.status}: ${response.statusText}`);
            }
            const data = await response.json();
            return {
                query,
                results: data.results || [],
                suggestions: data.suggestions || []
            };
        }
        catch (error) {
            throw new RegistryFetchError(`Failed to search for "${query}"`, error);
        }
    }
    /**
     * Get all capabilities with factory counts
     *
     * @returns Capabilities response
     */
    async getCapabilities() {
        const url = `${this.config.endpoint}/api/capabilities`;
        try {
            const response = await this.fetchWithRetry(url);
            if (!response.ok) {
                throw new Error(`Registry returned ${response.status}: ${response.statusText}`);
            }
            const data = await response.json();
            return data;
        }
        catch (error) {
            throw new RegistryFetchError('Failed to fetch capabilities', error);
        }
    }
    /**
     * Check if registry is accessible
     *
     * @returns True if registry is reachable
     */
    async checkHealth() {
        const url = `${this.config.endpoint}/health`;
        try {
            const response = await this.fetchWithRetry(url, 1); // Only one attempt
            return response.ok;
        }
        catch {
            return false;
        }
    }
    /**
     * Fetch with automatic retry and exponential backoff
     *
     * @param url - URL to fetch
     * @param attempt - Current attempt number
     * @returns Response object
     */
    async fetchWithRetry(url, attempt = 1) {
        try {
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
            const response = await fetch(url, {
                signal: controller.signal,
                headers: {
                    'User-Agent': this.config.userAgent || 'SpecVerse CLI',
                    'Accept': 'application/json'
                }
            });
            clearTimeout(timeoutId);
            return response;
        }
        catch (error) {
            // Check if we should retry
            if (attempt < this.config.retries) {
                // Exponential backoff: 1s, 2s, 3s, etc.
                await this.delay(1000 * attempt);
                return this.fetchWithRetry(url, attempt + 1);
            }
            // Max retries reached, throw error
            throw error;
        }
    }
    /**
     * Delay helper for retry backoff
     *
     * @param ms - Milliseconds to delay
     */
    delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    /**
     * Transform registry API response to internal format
     *
     * Handles differences between registry API format and internal types.
     *
     * @param data - Raw API response
     * @returns Transformed registry response
     */
    transformResponse(data) {
        return {
            results: (data.results || []).map((lib) => this.transformFactory(lib)),
            total: data.total || 0,
            page: data.page || 1,
            pageSize: data.pageSize || data.results?.length || 0
        };
    }
    /**
     * Transform single factory from API format to internal format
     *
     * @param lib - Raw library/factory data from API
     * @returns Transformed factory metadata
     */
    transformFactory(lib) {
        const factoryMeta = lib.factoryMeta || {};
        return {
            name: lib.name,
            displayName: lib.displayName || lib.name,
            category: lib.category || 'unknown',
            capabilities: factoryMeta.capabilities || [],
            framework: factoryMeta.framework || null,
            deprecated: lib.deprecated || false,
            deprecationReason: factoryMeta.deprecationReason,
            replacedBy: factoryMeta.replacedBy,
            alternatives: factoryMeta.alternatives || [],
            source: {
                type: factoryMeta.sourceType || 'npm',
                package: factoryMeta.sourcePackage,
                version: factoryMeta.sourceVersion,
                entrypoint: factoryMeta.sourceEntrypoint || 'factory.yaml',
                url: factoryMeta.sourceUrl,
                checksum: factoryMeta.sourceChecksum
            },
            tags: (lib.tags || []).map((t) => (typeof t === 'string' ? t : t.name)),
            description: lib.description || '',
            license: lib.license,
            repository: lib.repository,
            stats: {
                downloads: lib.downloadCount || 0,
                stars: lib.starCount || 0
            },
            createdAt: lib.createdAt || new Date().toISOString(),
            updatedAt: lib.updatedAt || new Date().toISOString()
        };
    }
    /**
     * Get the configured endpoint URL
     */
    getEndpoint() {
        return this.config.endpoint;
    }
    /**
     * Update client configuration
     *
     * @param config - Partial config to merge
     */
    updateConfig(config) {
        this.config = { ...this.config, ...config };
    }
}
/**
 * Registry fetch error
 *
 * Thrown when registry API requests fail.
 */
export class RegistryFetchError extends Error {
    cause;
    constructor(message, cause) {
        super(message);
        this.cause = cause;
        this.name = 'RegistryFetchError';
        // Preserve stack trace
        if (Error.captureStackTrace) {
            Error.captureStackTrace(this, RegistryFetchError);
        }
    }
    /**
     * Check if error is due to network issues
     */
    isNetworkError() {
        return (this.cause?.name === 'AbortError' ||
            this.cause?.name === 'TypeError' ||
            this.cause?.code === 'ECONNREFUSED' ||
            this.cause?.code === 'ENOTFOUND');
    }
    /**
     * Check if error is due to timeout
     */
    isTimeout() {
        return this.cause?.name === 'AbortError';
    }
    /**
     * Get user-friendly error message
     */
    getUserMessage() {
        if (this.isTimeout()) {
            return 'Request timed out. The registry may be slow or unreachable.';
        }
        if (this.isNetworkError()) {
            return 'Network error. Check your internet connection and try again.';
        }
        return this.message;
    }
}
//# sourceMappingURL=registry-client.js.map