import { A as getEnvString, o as logger } from "./logger-CfDSdx1q.js";
import { r as fetchWithTimeout } from "./fetch-CHx36OdV.js";
import { n as sleep } from "./time-DpwT58mY.js";
import { a as storeBlob } from "./blobs-0KeBpJTF.js";
import { C as getGoogleApiKey, D as resolveProjectId, E as loadCredentials, S as determineGoogleVertexMode, w as getGoogleClient } from "./util-BtjL1MRK.js";
import { t as ellipsize } from "./text-CFa1rdK5.js";
import fs from "fs";
import "crypto";
//#region src/providers/google/video.ts
/**
* Default location for Vertex AI
*/
const DEFAULT_LOCATION = "us-central1";
/**
* Valid durations by model family
*/
const VEO_3_DURATIONS = [
	4,
	6,
	8
];
const VEO_2_DURATIONS = [
	5,
	6,
	8
];
/**
* Default configuration values
*/
const DEFAULT_ASPECT_RATIO = "16:9";
const DEFAULT_RESOLUTION = "720p";
const DEFAULT_DURATION = 8;
const DEFAULT_POLL_INTERVAL_MS = 1e4;
const DEFAULT_MAX_POLL_TIME_MS = 6e5;
const REQUEST_TIMEOUT_MS = 3e5;
const AI_STUDIO_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
function validateAspectRatio(ratio) {
	if (!["16:9", "9:16"].includes(ratio)) return {
		valid: false,
		message: `Invalid aspect ratio "${ratio}". Valid ratios: 16:9, 9:16`
	};
	return { valid: true };
}
function validateDuration(model, duration) {
	const validDurations = model.includes("veo-2") ? VEO_2_DURATIONS : VEO_3_DURATIONS;
	if (!validDurations.includes(duration)) return {
		valid: false,
		message: `Invalid duration ${duration}s for ${model}. Valid: ${validDurations.join(", ")}s`
	};
	return { valid: true };
}
function validateResolution(model, aspectRatio, resolution) {
	if (model.includes("veo-3") && !model.includes("veo-3.1") && aspectRatio === "9:16") {
		if (resolution === "1080p") return {
			valid: false,
			message: `Veo 3 only supports 1080p for 16:9 aspect ratio. Use 720p for 9:16.`
		};
	}
	if (model.includes("veo-2") && resolution !== "720p") return {
		valid: false,
		message: `Veo 2 only supports 720p resolution.`
	};
	return { valid: true };
}
var GoogleVideoProvider = class {
	modelName;
	config;
	providerId;
	env;
	constructor(modelName, options = {}) {
		this.modelName = modelName;
		this.config = options.config || {};
		this.providerId = options.id;
		this.env = options.env;
	}
	id() {
		return this.providerId || `google:video:${this.modelName}`;
	}
	toString() {
		return `[Google Video Provider ${this.modelName}]`;
	}
	getLocation() {
		return this.config.region || getEnvString("GOOGLE_LOCATION") || this.env?.GOOGLE_LOCATION || DEFAULT_LOCATION;
	}
	async getProjectId() {
		return await resolveProjectId(this.config, this.env);
	}
	isVertexMode(config = this.config) {
		return determineGoogleVertexMode(config, this.env);
	}
	getApiKey(config = this.config) {
		const { apiKey } = getGoogleApiKey(config, this.env, this.isVertexMode(config));
		return apiKey;
	}
	async getClientWithCredentials() {
		const { client } = await getGoogleClient({ credentials: loadCredentials(this.config.credentials) });
		return client;
	}
	async getVertexEndpoint(action = "predictLongRunning") {
		const location = this.getLocation();
		return `https://${location}-aiplatform.googleapis.com/v1/projects/${await this.getProjectId()}/locations/${location}/publishers/google/models/${this.modelName}:${action}`;
	}
	getAiStudioEndpoint(pathSuffix) {
		return `${AI_STUDIO_BASE_URL}/${pathSuffix}`;
	}
	async getAiStudioHeaders(config) {
		const apiKey = this.getApiKey(config);
		if (!apiKey) throw new Error("Google API key is not set. Set GOOGLE_API_KEY or GEMINI_API_KEY, or add `apiKey` to the provider config.");
		return {
			"Content-Type": "application/json",
			"x-goog-api-key": apiKey
		};
	}
	/**
	* Load image data from file:// path or return as-is if base64
	*/
	loadImageData(imagePath) {
		if (imagePath.startsWith("file://")) {
			const filePath = imagePath.slice(7);
			if (!fs.existsSync(filePath)) return { error: `Image file not found: ${filePath}` };
			return { data: fs.readFileSync(filePath).toString("base64") };
		}
		return { data: imagePath };
	}
	/**
	* Load video data from file:// path or return as-is if base64
	*/
	loadVideoData(videoPath) {
		if (videoPath.startsWith("file://")) {
			const filePath = videoPath.slice(7);
			if (!fs.existsSync(filePath)) return { error: `Video file not found: ${filePath}` };
			return { data: fs.readFileSync(filePath).toString("base64") };
		}
		return { data: videoPath };
	}
	/**
	* Create a new video generation job
	*/
	async createVideoJob(prompt, config) {
		if (this.isVertexMode(config)) return this.createVertexVideoJob(prompt, config);
		return this.createAiStudioVideoJob(prompt, config);
	}
	buildVertexRequestBody(prompt, config) {
		const instance = { prompt };
		if (config.aspectRatio) instance.aspectRatio = config.aspectRatio;
		if (config.resolution) instance.resolution = config.resolution;
		if (config.durationSeconds) instance.durationSeconds = String(config.durationSeconds);
		if (config.negativePrompt) instance.negativePrompt = config.negativePrompt;
		if (config.personGeneration) instance.personGeneration = config.personGeneration;
		if (config.seed !== void 0) instance.seed = config.seed;
		if (config.image) {
			const { data: imageData, error } = this.loadImageData(config.image);
			if (error) return { error };
			instance.image = {
				imageBytes: imageData,
				mimeType: "image/png"
			};
		}
		const lastFrame = config.lastFrame || config.lastImage;
		if (lastFrame) {
			const { data: lastFrameData, error } = this.loadImageData(lastFrame);
			if (error) return { error };
			instance.lastFrame = {
				imageBytes: lastFrameData,
				mimeType: "image/png"
			};
		}
		if (config.referenceImages && config.referenceImages.length > 0) {
			const refs = [];
			for (const ref of config.referenceImages.slice(0, 3)) {
				const imagePath = typeof ref === "string" ? ref : ref.image;
				const referenceType = typeof ref === "string" ? "asset" : ref.referenceType || "asset";
				const { data: imageData, error } = this.loadImageData(imagePath);
				if (error) return { error };
				refs.push({
					image: {
						imageBytes: imageData,
						mimeType: "image/png"
					},
					referenceType
				});
			}
			instance.referenceImages = refs;
		}
		const extendVideoId = config.extendVideoId || config.sourceVideo;
		if (extendVideoId) instance.video = { operationName: extendVideoId };
		return { body: { instances: [instance] } };
	}
	buildAiStudioRequestBody(prompt, config) {
		const instance = { prompt };
		const parameters = {};
		if (config.aspectRatio) parameters.aspectRatio = config.aspectRatio;
		if (config.resolution) parameters.resolution = config.resolution;
		if (config.durationSeconds) parameters.durationSeconds = config.durationSeconds;
		if (config.negativePrompt) parameters.negativePrompt = config.negativePrompt;
		if (config.personGeneration) parameters.personGeneration = config.personGeneration;
		if (config.seed !== void 0) parameters.seed = config.seed;
		if (config.image) {
			const { data: imageData, error } = this.loadImageData(config.image);
			if (error) return { error };
			instance.image = { inlineData: {
				mimeType: "image/png",
				data: imageData
			} };
		}
		const lastFrame = config.lastFrame || config.lastImage;
		if (lastFrame) {
			const { data: lastFrameData, error } = this.loadImageData(lastFrame);
			if (error) return { error };
			instance.lastFrame = { inlineData: {
				mimeType: "image/png",
				data: lastFrameData
			} };
		}
		if (config.referenceImages && config.referenceImages.length > 0) {
			const refs = [];
			for (const ref of config.referenceImages.slice(0, 3)) {
				const imagePath = typeof ref === "string" ? ref : ref.image;
				const referenceType = typeof ref === "string" ? "asset" : ref.referenceType || "asset";
				const { data: imageData, error } = this.loadImageData(imagePath);
				if (error) return { error };
				refs.push({
					image: { inlineData: {
						mimeType: "image/png",
						data: imageData
					} },
					referenceType
				});
			}
			instance.referenceImages = refs;
		}
		const sourceVideo = config.extendVideoId || config.sourceVideo;
		if (sourceVideo) {
			if (sourceVideo.includes("/operations/")) return { error: "Google AI Studio Veo does not accept operation IDs for video extension. Use `vertex:video:*` with `extendVideoId`, or provide base64/file:// video data via `sourceVideo`." };
			const { data: videoData, error } = this.loadVideoData(sourceVideo);
			if (error) return { error };
			instance.video = { inlineData: {
				mimeType: "video/mp4",
				data: videoData
			} };
		}
		const body = { instances: [instance] };
		if (Object.keys(parameters).length > 0) body.parameters = parameters;
		return { body };
	}
	async createVertexVideoJob(prompt, config) {
		const url = await this.getVertexEndpoint("predictLongRunning");
		const { body, error: bodyError } = this.buildVertexRequestBody(prompt, config);
		if (bodyError || !body) return { error: bodyError || "Failed to build Vertex Veo request" };
		try {
			const client = await this.getClientWithCredentials();
			logger.debug("[Google Video] Creating video job", {
				url,
				model: this.modelName
			});
			return { operation: (await client.request({
				url,
				method: "POST",
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify(body)
			})).data };
		} catch (err) {
			const error = err;
			return { error: `Failed to create video job: ${error.response?.data?.error?.message || error.message || String(err)}` };
		}
	}
	async createAiStudioVideoJob(prompt, config) {
		const { body, error: bodyError } = this.buildAiStudioRequestBody(prompt, config);
		if (bodyError || !body) return { error: bodyError || "Failed to build Google AI Studio Veo request" };
		try {
			const headers = await this.getAiStudioHeaders(config);
			const url = this.getAiStudioEndpoint(`models/${this.modelName}:predictLongRunning`);
			logger.debug("[Google Video] Creating video job", {
				url,
				model: this.modelName,
				transport: "google-ai-studio"
			});
			const response = await fetchWithTimeout(url, {
				method: "POST",
				headers,
				body: JSON.stringify(body)
			}, REQUEST_TIMEOUT_MS);
			const data = await response.json();
			if (!response.ok) return { error: `Failed to create video job: ${data.error?.message || response.statusText}` };
			return { operation: data };
		} catch (err) {
			return { error: `Failed to create video job: ${err.message || String(err)}` };
		}
	}
	/**
	* Poll for video job completion using fetchPredictOperation endpoint
	*/
	async pollOperationStatus(operationName, pollIntervalMs, maxPollTimeMs, config) {
		if (this.isVertexMode(config)) return this.pollVertexOperationStatus(operationName, pollIntervalMs, maxPollTimeMs);
		return this.pollAiStudioOperationStatus(operationName, pollIntervalMs, maxPollTimeMs, config);
	}
	async pollVertexOperationStatus(operationName, pollIntervalMs, maxPollTimeMs) {
		const startTime = Date.now();
		const location = this.getLocation();
		const url = `https://${location}-aiplatform.googleapis.com/v1/projects/${await this.getProjectId()}/locations/${location}/publishers/google/models/${this.modelName}:fetchPredictOperation`;
		logger.debug(`[Google Video] Polling operation via fetchPredictOperation: ${url}`);
		const client = await this.getClientWithCredentials();
		while (Date.now() - startTime < maxPollTimeMs) try {
			const operation = (await client.request({
				url,
				method: "POST",
				headers: { "Content-Type": "application/json" },
				body: JSON.stringify({ operationName })
			})).data;
			logger.debug(`[Google Video] Operation status: done=${operation.done}, progress=${operation.metadata?.progress}%`);
			if (operation.done) {
				if (operation.error) return { error: `Video generation failed: ${operation.error.message}` };
				return { operation };
			}
			await sleep(pollIntervalMs);
		} catch (err) {
			const error = err;
			return { error: `Polling error: ${error.response?.data?.error?.message || error.message || String(err)}` };
		}
		return { error: `Video generation timed out after ${maxPollTimeMs / 1e3} seconds` };
	}
	async pollAiStudioOperationStatus(operationName, pollIntervalMs, maxPollTimeMs, config) {
		const startTime = Date.now();
		const url = this.getAiStudioEndpoint(operationName);
		const headers = await this.getAiStudioHeaders(config);
		logger.debug(`[Google Video] Polling operation via Google AI Studio: ${url}`);
		while (Date.now() - startTime < maxPollTimeMs) try {
			const response = await fetchWithTimeout(url, {
				method: "GET",
				headers
			}, REQUEST_TIMEOUT_MS);
			const operation = await response.json();
			if (!response.ok) return { error: `Polling error: ${operation.error?.message || response.statusText}` };
			logger.debug(`[Google Video] Operation status: done=${operation.done}, progress=${operation.metadata?.progress}%`);
			if (operation.done) {
				if (operation.error) return { error: `Video generation failed: ${operation.error.message}` };
				return { operation };
			}
			await sleep(pollIntervalMs);
		} catch (err) {
			return { error: `Polling error: ${err.message || String(err)}` };
		}
		return { error: `Video generation timed out after ${maxPollTimeMs / 1e3} seconds` };
	}
	/**
	* Download video from URI and store to blob storage
	*/
	async downloadVideoToBlob(videoUri, config) {
		if (this.isVertexMode(config)) return this.downloadVertexVideoToBlob(videoUri);
		return this.downloadAiStudioVideoToBlob(videoUri, config);
	}
	async downloadVertexVideoToBlob(videoUri) {
		try {
			const response = await (await this.getClientWithCredentials()).request({
				url: videoUri,
				method: "GET",
				responseType: "arraybuffer"
			});
			const { ref } = await storeBlob(Buffer.from(response.data), "video/mp4", {
				kind: "video",
				location: "response.video"
			});
			logger.debug(`[Google Video] Stored video to blob storage: ${ref.uri}`);
			return { blobRef: ref };
		} catch (err) {
			return { error: `Download error: ${err.message || String(err)}` };
		}
	}
	async downloadAiStudioVideoToBlob(videoUri, config) {
		try {
			const response = await fetchWithTimeout(videoUri, {
				method: "GET",
				headers: await this.getAiStudioHeaders(config)
			}, REQUEST_TIMEOUT_MS);
			if (!response.ok) return { error: `Download error: ${response.statusText}` };
			const { ref } = await storeBlob(Buffer.from(await response.arrayBuffer()), "video/mp4", {
				kind: "video",
				location: "response.video"
			});
			logger.debug(`[Google Video] Stored video to blob storage: ${ref.uri}`);
			return { blobRef: ref };
		} catch (err) {
			return { error: `Download error: ${err.message || String(err)}` };
		}
	}
	/**
	* Store base64 encoded video to blob storage
	*/
	async storeBase64VideoToBlob(base64Data) {
		try {
			const { ref, deduplicated } = await storeBlob(Buffer.from(base64Data, "base64"), "video/mp4", {
				kind: "video",
				location: "response.video"
			});
			logger.debug(`[Google Video] Stored video to blob storage: ${ref.uri} (deduplicated: ${deduplicated})`);
			return { blobRef: ref };
		} catch (err) {
			return { error: `Save error: ${err.message || String(err)}` };
		}
	}
	async callApi(prompt, context) {
		if (!prompt || prompt.trim() === "") return { error: "Prompt is required for video generation" };
		let effectiveConfig = {
			...this.config,
			...context?.prompt?.config
		};
		if (this.isVertexMode(effectiveConfig)) {
			let projectId = effectiveConfig.projectId || getEnvString("GOOGLE_CLOUD_PROJECT") || getEnvString("GOOGLE_PROJECT_ID") || this.env?.GOOGLE_CLOUD_PROJECT || this.env?.GOOGLE_PROJECT_ID;
			if (!projectId) try {
				projectId = await resolveProjectId(effectiveConfig, this.env);
			} catch {
				return { error: "Google Veo video generation via Vertex AI requires a project ID. Set GOOGLE_CLOUD_PROJECT or add `projectId` to the provider config, then run \"gcloud auth application-default login\"." };
			}
			effectiveConfig = {
				...effectiveConfig,
				vertexai: true,
				...projectId ? { projectId } : {}
			};
		} else if (!this.getApiKey(effectiveConfig)) try {
			const adcProjectId = await resolveProjectId(effectiveConfig, this.env);
			if (adcProjectId) effectiveConfig = {
				...effectiveConfig,
				vertexai: true,
				projectId: adcProjectId
			};
			else return { error: "Google Veo video generation via Google AI Studio requires an API key. Set GOOGLE_API_KEY or GEMINI_API_KEY, or add `apiKey` to the provider config." };
		} catch {
			return { error: "Google Veo video generation via Google AI Studio requires an API key. Set GOOGLE_API_KEY or GEMINI_API_KEY, or add `apiKey` to the provider config." };
		}
		const model = effectiveConfig.model || this.modelName;
		const aspectRatio = effectiveConfig.aspectRatio || DEFAULT_ASPECT_RATIO;
		const resolution = effectiveConfig.resolution || DEFAULT_RESOLUTION;
		const durationSeconds = effectiveConfig.durationSeconds || effectiveConfig.duration || DEFAULT_DURATION;
		const ratioValidation = validateAspectRatio(aspectRatio);
		if (!ratioValidation.valid) return { error: ratioValidation.message };
		const durationValidation = validateDuration(model, durationSeconds);
		if (!durationValidation.valid) return { error: durationValidation.message };
		const resolutionValidation = validateResolution(model, aspectRatio, resolution);
		if (!resolutionValidation.valid) return { error: resolutionValidation.message };
		const startTime = Date.now();
		logger.info(`[Google Video] Creating video job for model ${model}...`);
		const { operation: createdOp, error: createError } = await this.createVideoJob(prompt, {
			...effectiveConfig,
			aspectRatio,
			resolution,
			durationSeconds
		});
		if (createError || !createdOp) return { error: createError || "Failed to create video job" };
		const operationName = createdOp.name;
		logger.info(`[Google Video] Video job created: ${operationName}`);
		const pollIntervalMs = effectiveConfig.pollIntervalMs || DEFAULT_POLL_INTERVAL_MS;
		const maxPollTimeMs = effectiveConfig.maxPollTimeMs || DEFAULT_MAX_POLL_TIME_MS;
		const { operation: completedOp, error: pollError } = await this.pollOperationStatus(operationName, pollIntervalMs, maxPollTimeMs, effectiveConfig);
		if (pollError || !completedOp) return { error: pollError || "Polling failed" };
		let blobRef;
		const base64Video = completedOp.response?.videos?.[0]?.bytesBase64Encoded;
		if (base64Video) {
			logger.debug(`[Google Video] Storing base64 encoded video to blob storage...`);
			const { blobRef: ref, error } = await this.storeBase64VideoToBlob(base64Video);
			if (error) return { error };
			blobRef = ref;
		} else {
			const videoUri = completedOp.response?.generateVideoResponse?.generatedSamples?.[0]?.video?.uri;
			if (!videoUri) {
				logger.debug(`[Google Video] Response: ${JSON.stringify(completedOp.response)}`);
				return { error: "No video data in response" };
			}
			const { blobRef: ref, error: downloadError } = await this.downloadVideoToBlob(videoUri, effectiveConfig);
			if (downloadError) return { error: downloadError };
			blobRef = ref;
		}
		if (!blobRef) return { error: "Failed to store video" };
		const latencyMs = Date.now() - startTime;
		return {
			output: `[Video: ${ellipsize(prompt.replace(/\r?\n|\r/g, " ").replace(/\[/g, "(").replace(/\]/g, ")"), 50)}](${blobRef.uri})`,
			cached: false,
			latencyMs,
			video: {
				id: operationName,
				blobRef,
				url: blobRef.uri,
				format: "mp4",
				size: resolution,
				duration: durationSeconds,
				model,
				aspectRatio,
				resolution
			},
			metadata: {
				operationName,
				model,
				aspectRatio,
				resolution,
				durationSeconds,
				blobHash: blobRef.hash
			}
		};
	}
};
//#endregion
export { GoogleVideoProvider };

//# sourceMappingURL=video-B4dMA3N-.js.map