import { createLogger } from "../../utils/logger/index.js";
import { init_logger } from "../../utils/logger.js";
import { resolveStateDir } from "../../config/paths-state.js";
import { init_paths, resolveExtensionsDir } from "../../config/paths.js";
import { createSkillConfigManager } from "../../agent/skills/config.js";
import { removeSkillsLockEntry } from "../../agent/skills/hub-lock.js";
import { deleteManagedSkill, installSkillFromZip, listManagedSkillDirs } from "../../agent/skills/managed-store.js";
import { listRegisteredProviders } from "../../agent/skills/marketplace/registry.js";
import { getExtensionLockfileManager } from "../../extensions/lockfile.js";
import { installExtensionFromStoreZip, peekExtensionIdFromStoreZip } from "../../extensions/install.js";
import { downloadExtensionStoreZipBuffer, fetchMarketplacePackageDetail, resolveExtensionZipDownloadUrl, resolveExtensionsStoreBaseUrl } from "../../agent/skills/marketplace/adapters/store/store-api-client.js";
import { getMarketplaceProviderDisplayName, resolveSkillsMarketplaceProvider } from "../../agent/skills/marketplace/resolve-adapter.js";
import { downloadFromMarketplace, getMarketplacePackageDetail, listMarketplaceCategories, listMarketplacePackages } from "../../agent/skills/skills-marketplace.js";
import { existsSync, mkdirSync, rmSync } from "node:fs";
import { join } from "node:path";
//#region src/gateway/service/marketplace-service.ts
/**
* GatewayMarketplaceService — install / browse / remove for the two marketplaces
* the gateway exposes:
*
*   • **Skills** (`~/.xopc/skills/managed/<id>`) — zip-bundled markdown skills
*     pulled from a provider catalog (`agent/skills/skills-marketplace.ts`).
*   • **Extensions** (`~/.xopc/extensions/<id>`) — full extension packages from
*     the xopc-store.
*
* Owns the install/uninstall composite operations:
*   - download zip → unpack → upsert lockfile → refresh loader → emit reload
*   - rm dir → remove from `extensions.enabled` → refresh loader → emit reload
*
* Local-only skill management (install-from-zip, delete, enable/disable, list)
* lives here too so callers (commands-skills routes) depend on a single narrow
* service instead of the full `GatewayService`.
*/
init_paths();
init_logger();
const log = createLogger("GatewayMarketplace");
var GatewayMarketplaceService = class {
	opts;
	constructor(opts) {
		this.opts = opts;
	}
	getSkillsApi(lang) {
		return {
			catalog: this.opts.getAgentService().getSkillCatalog(lang),
			managed: listManagedSkillDirs()
		};
	}
	getSkillMarkdownSource(skillName, lang) {
		return this.opts.getAgentService().getSkillMarkdownSource(skillName, lang);
	}
	deleteSkill(skillId) {
		removeSkillsLockEntry(skillId);
		deleteManagedSkill(skillId);
		this.opts.getAgentService().refreshSkillsAfterDiskChange();
	}
	installSkillZip(buffer, opts) {
		const result = installSkillFromZip(buffer, opts);
		removeSkillsLockEntry(result.skillId);
		this.opts.getAgentService().refreshSkillsAfterDiskChange();
		return result;
	}
	reloadSkills() {
		this.opts.getAgentService().refreshSkillsAfterDiskChange();
	}
	patchSkillEnabled(skillName, enabled) {
		createSkillConfigManager(resolveStateDir()).setSkillEnabled(skillName, enabled);
		this.opts.getAgentService().refreshSkillsAfterSkillConfigChange();
	}
	async fetchSkillsCatalog(params, provider) {
		return listMarketplacePackages(this.opts.getConfig(), params, provider);
	}
	async fetchSkillsCategories(provider) {
		return listMarketplaceCategories(this.opts.getConfig(), provider);
	}
	async fetchSkillsPackageDetail(packageName, provider) {
		return getMarketplacePackageDetail(this.opts.getConfig(), packageName, provider);
	}
	async installSkill(opts) {
		const { buffer, skillId } = await downloadFromMarketplace(this.opts.getConfig(), opts.name, opts.version, opts.provider);
		return this.installSkillZip(buffer, {
			skillId,
			overwrite: opts.overwrite ?? false
		});
	}
	getSkillsProvider() {
		const provider = resolveSkillsMarketplaceProvider(this.opts.getConfig());
		return {
			provider,
			displayName: getMarketplaceProviderDisplayName(provider)
		};
	}
	/** All registered marketplace providers (built-in + extension-contributed). */
	getSkillsProviders() {
		return listRegisteredProviders();
	}
	/** xopc-store extension package preview (type must be `extension`). */
	async fetchExtensionPackageDetail(packageName) {
		const detail = await fetchMarketplacePackageDetail(resolveExtensionsStoreBaseUrl(this.opts.getConfig()), packageName.trim());
		if (detail.type !== "extension") throw new Error(`Package "${packageName}" is not an extension (store type: ${detail.type}).`);
		return detail;
	}
	/**
	* Install an extension from xopc-store into `~/.xopc/extensions`, append id
	* to `extensions.enabled`, refresh the loader, and emit `config.reload`.
	* Returns `requiresGatewayRestart=true` when a new channel plugin would have
	* to wire into the running gateway (channel registration cannot hot-patch).
	*/
	async installExtension(opts) {
		const packageName = opts.name.trim();
		if (!packageName) throw new Error("Package name is required");
		const cfg = this.opts.getConfig();
		const storeBase = resolveExtensionsStoreBaseUrl(cfg);
		const targetDir = resolveExtensionsDir();
		mkdirSync(targetDir, { recursive: true });
		const { downloadUrl, version } = await resolveExtensionZipDownloadUrl(storeBase, packageName, opts.version);
		const buf = await downloadExtensionStoreZipBuffer(storeBase, downloadUrl);
		if (opts.overwrite) {
			const peekId = peekExtensionIdFromStoreZip(buf);
			if (peekId && existsSync(join(targetDir, peekId))) rmSync(join(targetDir, peekId), {
				recursive: true,
				force: true
			});
		}
		const result = await installExtensionFromStoreZip(buf, targetDir);
		if (!result.ok || !result.extensionId) throw new Error(result.error ?? "Extension install failed");
		await getExtensionLockfileManager().upsert(result.extensionId, {
			name: result.extensionId,
			version,
			resolved: packageName,
			source: "store"
		});
		const nextConfig = this.mergeExtensionEnabledIntoConfig(cfg, result.extensionId);
		const saved = await this.opts.saveConfig(nextConfig);
		if (!saved.saved) throw new Error(saved.error ?? "Failed to save config after extension install");
		const channelIdsBefore = new Set(this.opts.getChannelManager().getAllPlugins().map((p) => p.id));
		let requiresGatewayRestart = false;
		const loader = this.opts.getExtensionLoader();
		try {
			if (loader) {
				loader.invalidateManifestCache();
				await loader.loadByActivationPlan();
				const reg = loader.getRegistry();
				for (const p of reg.channelPlugins) if (!channelIdsBefore.has(p.id)) {
					requiresGatewayRestart = true;
					break;
				}
			}
		} catch (err) {
			const em = err instanceof Error ? err.message : String(err);
			log.warn({
				err,
				errorMessage: em
			}, `Extension loader refresh after marketplace install failed: ${em}`);
			requiresGatewayRestart = true;
		}
		this.opts.emit("config.reload", {
			section: "extensions",
			source: "marketplace-install"
		});
		return {
			extensionId: result.extensionId,
			version,
			requiresGatewayRestart
		};
	}
	/** Remove a user-installed extension (global or per-agent dir) from disk and config. */
	async uninstallExtension(extensionId) {
		const id = extensionId.trim();
		if (!id) throw new Error("extensionId is required");
		const loader = this.opts.getExtensionLoader();
		if (!loader) throw new Error("Extensions unavailable");
		const ext = loader.discoverExtensions().find((e) => e.id === id);
		if (!ext) throw new Error(`Extension not found: ${id}`);
		if (ext.source === "bundled") throw new Error("Built-in extensions cannot be uninstalled from the marketplace UI");
		if (existsSync(ext.path)) rmSync(ext.path, {
			recursive: true,
			force: true
		});
		await getExtensionLockfileManager().remove(id);
		const nextConfig = this.mergeExtensionRemovedFromEnabledConfig(this.opts.getConfig(), id);
		const saved = await this.opts.saveConfig(nextConfig);
		if (!saved.saved) throw new Error(saved.error ?? "Failed to save config after extension uninstall");
		try {
			loader.invalidateManifestCache();
			await loader.loadByActivationPlan();
		} catch (err) {
			const em = err instanceof Error ? err.message : String(err);
			log.warn({
				err,
				errorMessage: em
			}, `Extension loader refresh after uninstall failed: ${em}`);
		}
		this.opts.emit("config.reload", {
			section: "extensions",
			source: "marketplace-uninstall"
		});
		return { requiresGatewayRestart: true };
	}
	mergeExtensionEnabledIntoConfig(currentConfig, extensionId) {
		const id = extensionId.trim();
		const prevExt = currentConfig.extensions;
		const baseExt = prevExt && typeof prevExt === "object" && !Array.isArray(prevExt) ? { ...prevExt } : {};
		const enabledRaw = baseExt.enabled;
		const enabled = Array.isArray(enabledRaw) ? [...enabledRaw.filter((x) => typeof x === "string")] : [];
		if (!enabled.includes(id)) enabled.push(id);
		const disabledRaw = baseExt.disabled;
		const nextExt = {
			...baseExt,
			enabled
		};
		if (Array.isArray(disabledRaw)) {
			const next = disabledRaw.filter((x) => typeof x === "string" && x !== id);
			if (next.length > 0) nextExt.disabled = next;
			else delete nextExt.disabled;
		}
		return {
			...currentConfig,
			extensions: nextExt
		};
	}
	mergeExtensionRemovedFromEnabledConfig(currentConfig, extensionId) {
		const id = extensionId.trim();
		const prevExt = currentConfig.extensions;
		const baseExt = prevExt && typeof prevExt === "object" && !Array.isArray(prevExt) ? { ...prevExt } : {};
		const enabledRaw = baseExt.enabled;
		const enabled = Array.isArray(enabledRaw) ? enabledRaw.filter((x) => typeof x === "string" && x !== id) : [];
		return {
			...currentConfig,
			extensions: {
				...baseExt,
				enabled
			}
		};
	}
};
//#endregion
export { GatewayMarketplaceService };

//# sourceMappingURL=marketplace-service.js.map