import { Configuration } from './configuration.js';
import { CoreInterop } from './detail/coreInterop.js';
import { ModelLoadManager } from './detail/modelLoadManager.js';
import { Catalog } from './catalog.js';
import { ResponsesClient } from './openai/responsesClient.js';
/**
 * The main entry point for the Foundry Local SDK.
 * Manages the initialization of the core system and provides access to the Catalog and ModelLoadManager.
 */
export class FoundryLocalManager {
    static instance;
    config;
    coreInterop;
    _modelLoadManager;
    _catalog;
    _urls = [];
    constructor(config) {
        this.config = config;
        this.coreInterop = new CoreInterop(this.config);
        this.coreInterop.executeCommand("initialize", { Params: this.config.params });
        this._modelLoadManager = new ModelLoadManager(this.coreInterop);
        this._catalog = new Catalog(this.coreInterop, this._modelLoadManager);
    }
    /**
     * Creates the FoundryLocalManager singleton with the provided configuration.
     * @param config - The configuration settings for the SDK (plain object).
     * @returns The initialized FoundryLocalManager instance.
     * @example
     * ```typescript
     * const manager = FoundryLocalManager.create({
     *   appName: 'MyApp',
     *   logLevel: 'info'
     * });
     * ```
     */
    static create(config) {
        if (!FoundryLocalManager.instance) {
            const internalConfig = new Configuration(config);
            FoundryLocalManager.instance = new FoundryLocalManager(internalConfig);
        }
        return FoundryLocalManager.instance;
    }
    /**
     * Gets the Catalog instance for discovering and managing models.
     * @returns The Catalog instance.
     */
    get catalog() {
        return this._catalog;
    }
    /**
     * Gets the URLs where the web service is listening.
     * Returns an empty array if the web service is not running.
     * @returns An array of URLs.
     */
    get urls() {
        return this._urls;
    }
    /**
     * Starts the local web service.
     * Use the `urls` property to retrieve the bound addresses after the service has started.
     * If no listener address is configured, the service defaults to `127.0.0.1:0` (binding to a random ephemeral port).
     * @throws Error - If starting the service fails.
     */
    startWebService() {
        const response = this.coreInterop.executeCommand("start_service");
        try {
            this._urls = JSON.parse(response);
        }
        catch (error) {
            throw new Error(`Failed to decode JSON response from start_service: ${error}. Response was: ${response}`);
        }
    }
    /**
     * Stops the local web service.
     * @throws Error - If stopping the service fails.
     */
    stopWebService() {
        if (this._urls.length > 0) {
            this.coreInterop.executeCommand("stop_service");
            this._urls = [];
        }
    }
    /**
     * Whether the web service is currently running.
     */
    get isWebServiceRunning() {
        return this._urls.length > 0;
    }
    /**
     * Discovers available execution providers (EPs) and their registration status.
     * @returns An array of EpInfo describing each available EP.
     */
    discoverEps() {
        const response = this.coreInterop.executeCommand("discover_eps");
        try {
            const raw = JSON.parse(response);
            return raw.map((ep) => ({
                name: ep.Name,
                isRegistered: ep.IsRegistered
            }));
        }
        catch (error) {
            throw new Error(`Failed to decode JSON response from discover_eps: ${error}. Response was: ${response}`);
        }
    }
    async downloadAndRegisterEps(namesOrCallback, progressCallback) {
        let names;
        if (typeof namesOrCallback === 'function') {
            progressCallback = namesOrCallback;
        }
        else {
            names = namesOrCallback;
        }
        const params = {};
        if (names && names.length > 0) {
            params.Params = { Names: names.join(",") };
        }
        let response;
        if (progressCallback) {
            response = await this.coreInterop.executeCommandStreaming("download_and_register_eps", Object.keys(params).length > 0 ? params : undefined, (chunk) => {
                const sepIndex = chunk.indexOf('|');
                if (sepIndex >= 0) {
                    const epName = chunk.substring(0, sepIndex);
                    const percent = parseFloat(chunk.substring(sepIndex + 1));
                    if (!isNaN(percent)) {
                        progressCallback(epName || '', percent);
                    }
                }
            });
        }
        else {
            response = await this.coreInterop.executeCommandStreaming("download_and_register_eps", Object.keys(params).length > 0 ? params : undefined, () => { } // no-op callback
            );
        }
        let epResult;
        try {
            const raw = JSON.parse(response);
            epResult = {
                success: raw.Success,
                status: raw.Status,
                registeredEps: raw.RegisteredEps,
                failedEps: raw.FailedEps
            };
        }
        catch (error) {
            throw new Error(`Failed to decode JSON response from download_and_register_eps: ${error}. Response was: ${response}`);
        }
        // Invalidate the catalog cache if any EP was newly registered so the next access
        // re-fetches models with the updated set of available EPs.
        if (epResult.success || epResult.registeredEps.length > 0) {
            this._catalog.invalidateCache();
        }
        return epResult;
    }
    /**
     * Creates a ResponsesClient for interacting with the Responses API.
     * The web service must be started first via `startWebService()`.
     * @param modelId - Optional default model ID for requests.
     * @returns A ResponsesClient instance.
     * @throws Error - If the web service is not running.
     */
    createResponsesClient(modelId) {
        if (this._urls.length === 0) {
            throw new Error('Web service is not running. Call startWebService() before creating a ResponsesClient.');
        }
        return new ResponsesClient(this._urls[0], modelId);
    }
}
