import { regEx } from "../../../util/regex.js";
import { logger } from "../../../logger/index.js";
import { joinUrlParts } from "../../../util/url.js";
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
import { withCache } from "../../../util/cache/package/with-cache.js";
import { asTimestamp } from "../../../util/timestamp.js";
import { map } from "../../../util/promises.js";
import { id } from "../../versioning/hashicorp/index.js";
import { TerraformDatasource } from "../terraform-module/base.js";
import { createSDBackendURL } from "../terraform-module/utils.js";
//#region lib/modules/datasource/terraform-provider/index.ts
var TerraformProviderDatasource = class TerraformProviderDatasource extends TerraformDatasource {
	static id = "terraform-provider";
	static hashicorpReleaseUrl = "https://releases.hashicorp.com";
	static terraformRegistryUrl = "https://registry.terraform.io";
	static defaultRegistryUrls = [TerraformProviderDatasource.terraformRegistryUrl, TerraformProviderDatasource.hashicorpReleaseUrl];
	static repositoryRegex = regEx(/^hashicorp\/(?<packageName>\S+)$/);
	constructor() {
		super(TerraformProviderDatasource.id);
	}
	defaultRegistryUrls = TerraformProviderDatasource.defaultRegistryUrls;
	defaultVersioning = id;
	registryStrategy = "hunt";
	releaseTimestampSupport = true;
	releaseTimestampNote = "The release timestamp is only supported for the latest version, and is determined from the `published_at` field in the results and only for `https://registry.terraform.io`";
	sourceUrlSupport = "package";
	sourceUrlNote = "The source URL is determined from the the `source` field in the results.";
	async _getReleases({ packageName, registryUrl }) {
		/* v8 ignore next 3 -- should never happen */
		if (!registryUrl) return null;
		logger.trace(`terraform-provider.getDependencies() packageName: ${packageName}`);
		if (registryUrl === TerraformProviderDatasource.terraformRegistryUrl) {
			const repository = TerraformProviderDatasource.getRepository({ packageName });
			const serviceDiscovery = await this.getTerraformServiceDiscoveryResult(registryUrl);
			return await this.queryTerraformRegistry(serviceDiscovery, registryUrl, repository);
		}
		if (registryUrl === TerraformProviderDatasource.hashicorpReleaseUrl) return await this.queryReleaseBackend(packageName, registryUrl);
		return await this.queryProviderRegistry(registryUrl, packageName);
	}
	getReleases(config) {
		const url = config.registryUrl;
		const repo = TerraformProviderDatasource.getRepository(config);
		return withCache({
			namespace: `datasource-${TerraformProviderDatasource.id}`,
			key: `getReleases:${url}/${repo}`,
			fallback: true
		}, () => this._getReleases(config));
	}
	static getRepository({ packageName }) {
		return packageName.includes("/") ? packageName : `hashicorp/${packageName}`;
	}
	/**
	* Query the Terraform Registry using the undocumented extended provider API.
	* This provides more information than the base Provider Registry Protocol,
	* such as the release date for the latest version and the source URL.
	*/
	async queryTerraformRegistry(serviceDiscovery, registryUrl, repository) {
		const backendURL = createSDBackendURL(registryUrl, "providers.v1", serviceDiscovery, repository);
		const res = (await this.http.getJsonUnchecked(backendURL)).body;
		const dep = { releases: res.versions.map((version) => ({ version })) };
		if (res.source) dep.sourceUrl = res.source;
		const latestVersion = dep.releases.find((release) => res.version === release.version);
		if (latestVersion) latestVersion.releaseTimestamp = asTimestamp(res.published_at);
		dep.homepage = `${registryUrl}/providers/${repository}`;
		return dep;
	}
	/**
	* Query a registry using the Provider Registry Protocol that all registries
	* are required to implement.
	* https://www.terraform.io/internals/provider-registry-protocol
	*/
	async queryProviderRegistry(registryUrl, packageName) {
		const repository = TerraformProviderDatasource.getRepository({ packageName });
		const backendURL = createSDBackendURL(registryUrl, "providers.v1", await this.getTerraformServiceDiscoveryResult(registryUrl), `${repository}/versions`);
		return { releases: (await this.http.getJsonUnchecked(backendURL)).body.versions.map(({ version }) => ({ version })) };
	}
	async queryReleaseBackend(packageName, registryURL) {
		const backendLookUpName = `terraform-provider-${packageName.replace("hashicorp/", "")}`;
		const backendURL = joinUrlParts(registryURL, backendLookUpName, `index.json`);
		const res = (await this.http.getJsonUnchecked(backendURL)).body;
		return {
			releases: Object.keys(res.versions).map((version) => ({ version })),
			sourceUrl: joinUrlParts("https://github.com/terraform-providers", backendLookUpName)
		};
	}
	async _getBuilds(registryURL, repository, version) {
		if (registryURL === TerraformProviderDatasource.hashicorpReleaseUrl) {
			const repositoryRegexResult = TerraformProviderDatasource.repositoryRegex.exec(repository)?.groups;
			if (!repositoryRegexResult) return null;
			const backendLookUpName = `terraform-provider-${repositoryRegexResult.packageName}`;
			let versionReleaseBackend;
			try {
				versionReleaseBackend = await this.getReleaseBackendIndex(backendLookUpName, version);
			} catch (err) {
				if (err instanceof ExternalHostError) throw err;
				logger.debug({
					err,
					backendLookUpName,
					version
				}, `Failed to retrieve builds for ${backendLookUpName} ${version}`);
				throw new ExternalHostError(err);
			}
			return versionReleaseBackend.builds;
		}
		const serviceDiscovery = await this.getTerraformServiceDiscoveryResult(registryURL);
		if (!serviceDiscovery) throw new ExternalHostError(/* @__PURE__ */ new Error(`Service discovery not found for ${registryURL}`));
		const backendURL = createSDBackendURL(registryURL, "providers.v1", serviceDiscovery, repository);
		const versionsResponse = (await this.http.getJsonUnchecked(`${backendURL}/versions`)).body;
		if (!versionsResponse.versions) throw new ExternalHostError(/* @__PURE__ */ new Error(`Failed to retrieve version list for ${backendURL}`));
		const builds = versionsResponse.versions.find((value) => value.version === version);
		if (!builds) throw new ExternalHostError(/* @__PURE__ */ new Error(`No builds found for ${repository}:${version} on ${registryURL}`));
		return await map(builds.platforms, async (platform) => {
			const buildURL = `${backendURL}/${version}/download/${platform.os}/${platform.arch}`;
			try {
				const res = (await this.http.getJsonUnchecked(buildURL)).body;
				return {
					name: repository,
					url: res.download_url,
					version,
					...res
				};
			} catch (err) {
				/* v8 ignore next 3 -- hard to test */
				if (err instanceof ExternalHostError) throw err;
				logger.debug({
					err,
					url: buildURL
				}, "Failed to retrieve build");
				throw new ExternalHostError(err);
			}
		}, { concurrency: 4 });
	}
	getBuilds(registryURL, repository, version) {
		return withCache({
			namespace: `datasource-${TerraformProviderDatasource.id}`,
			key: `getBuilds:${registryURL}/${repository}/${version}`
		}, () => this._getBuilds(registryURL, repository, version));
	}
	async _getZipHashes(zipHashUrl) {
		let rawHashData;
		try {
			rawHashData = (await this.http.getText(zipHashUrl)).body;
		} catch (err) {
			/* v8 ignore next 3 -- hard to test */
			if (err instanceof ExternalHostError) throw err;
			logger.debug({
				err,
				zipHashUrl
			}, `Failed to retrieve zip hashes from ${zipHashUrl}`);
			return;
		}
		return rawHashData.trimEnd().split("\n").map((line) => line.split(/\s/)[0]);
	}
	getZipHashes(zipHashUrl) {
		return withCache({
			namespace: `datasource-${TerraformProviderDatasource.id}`,
			key: `getZipHashes:${zipHashUrl}`
		}, () => this._getZipHashes(zipHashUrl));
	}
	async _getReleaseBackendIndex(backendLookUpName, version) {
		return (await this.http.getJsonUnchecked(`${TerraformProviderDatasource.hashicorpReleaseUrl}/${backendLookUpName}/${version}/index.json`)).body;
	}
	getReleaseBackendIndex(backendLookUpName, version) {
		return withCache({
			namespace: `datasource-${TerraformProviderDatasource.id}`,
			key: `getReleaseBackendIndex:${backendLookUpName}/${version}`
		}, () => this._getReleaseBackendIndex(backendLookUpName, version));
	}
};
//#endregion
export { TerraformProviderDatasource };

//# sourceMappingURL=index.js.map