import { createAction, Property, DynamicPropsValue } from '@activepieces/pieces-framework';
import { propsValidation } from '@activepieces/pieces-common';
import { runwayAuth } from '../common';
import RunwayML from '@runwayml/sdk';
import * as z from 'zod/mini'

export const generateVideoFromImage = createAction({
	auth: runwayAuth,
	name: 'generate_video_from_image',
	displayName: 'Generate Video From Image',
	description: 'Generates a video based on image(s) and text prompt using Runway\'s AI models',
	audience: 'both',
	aiMetadata: { description: 'Submits an image-to-video generation job to Runway from a starting frame and optional prompt, returning the created task ID; the video is produced asynchronously, so poll Get Task Details with that ID for the result. Provide the starting frame as exactly one of an uploaded file or an HTTPS image URL (not both). Choose when an agent needs to animate an image into a clip. Each call starts a new billable generation, so it is not idempotent.', idempotent: false },
	props: {
		model: Property.StaticDropdown({
			displayName: 'Model',
			description: 'AI model to use for video generation',
			required: true,
			options: {
				options: [
					{ label: 'Gen4 Turbo (Fast, High Quality)', value: 'gen4_turbo' },
					{ label: 'Gen3a Turbo (Balanced)', value: 'gen3a_turbo' },
					{ label: 'Veo3 (Google\'s Latest)', value: 'veo3' },
				],
			},
		}),
		promptImageFile: Property.File({ 
			displayName: 'Prompt Image File', 
			description: 'Upload an image file to use as the video\'s starting frame',
			required: false 
		}),
		promptImageUrl: Property.ShortText({ 
			displayName: 'Prompt Image URL', 
			description: 'HTTPS URL of an image to use as the video\'s starting frame',
			required: false 
		}),
		imagePosition: Property.StaticDropdown({
			displayName: 'Image Position',
			description: 'Position of the image in the video (last frame only supported by gen3a_turbo)',
			required: false,
			defaultValue: 'first',
			options: {
				options: [
					{ label: 'First Frame', value: 'first' },
					{ label: 'Last Frame (gen3a_turbo only)', value: 'last' },
				],
			},
		}),
		promptText: Property.LongText({ 
			displayName: 'Prompt Text (Optional)', 
			description: 'Describe what should happen in the video (max 1000 characters)',
			required: false 
		}),
		ratio: Property.DynamicProperties({
			auth: runwayAuth,
			displayName: 'Video Resolution',
			description: 'Available resolutions depend on the selected model',
			required: true,
			refreshers: ['model'],
			props: async ({ model }) => {
				const ratioOptions: DynamicPropsValue = {};
				
				if ((model as unknown as string) === 'gen4_turbo') {
					ratioOptions['ratio'] = Property.StaticDropdown({
						displayName: 'Resolution',
						required: true,
						options: {
							options: [
								{ label: 'HD Landscape (1280x720)', value: '1280:720' },
								{ label: 'HD Portrait (720x1280)', value: '720:1280' },
								{ label: 'Wide (1104x832)', value: '1104:832' },
								{ label: 'Tall (832x1104)', value: '832:1104' },
								{ label: 'Square (960x960)', value: '960:960' },
								{ label: 'Ultra Wide (1584x672)', value: '1584:672' },
							],
						},
					});
				} else if ((model as unknown as string) === 'gen3a_turbo') {
					ratioOptions['ratio'] = Property.StaticDropdown({
						displayName: 'Resolution',
						required: true,
						options: {
							options: [
								{ label: 'Landscape (1280x768)', value: '1280:768' },
								{ label: 'Portrait (768x1280)', value: '768:1280' },
							],
						},
					});
				} else if ((model as unknown as string) === 'veo3') {
					ratioOptions['ratio'] = Property.StaticDropdown({
						displayName: 'Resolution',
						required: true,
						options: {
							options: [
								{ label: 'HD Landscape (1280x720)', value: '1280:720' },
								{ label: 'HD Portrait (720x1280)', value: '720:1280' },
							],
						},
					});
				} else {
					ratioOptions['ratio'] = Property.StaticDropdown({
						displayName: 'Resolution',
						required: true,
						options: {
							options: [
								{ label: 'Select a model first', value: '' },
							],
						},
					});
				}
				
				return ratioOptions;
			},
		}),
		duration: Property.DynamicProperties({
			auth: runwayAuth,
			displayName: 'Video Duration',
			description: 'Available durations depend on the selected model',
			required: true,
			refreshers: ['model'],
			props: async ({ model }) => {
				const durationOptions: DynamicPropsValue = {};
				
				if ((model as unknown as string) === 'veo3') {
					durationOptions['duration'] = Property.StaticDropdown({
						displayName: 'Duration (seconds)',
						required: true,
						defaultValue: 8,
						options: {
							options: [
								{ label: '8 seconds (required for veo3)', value: 8 },
							],
						},
					});
				} else if ((model as unknown as string) === 'gen4_turbo' || (model as unknown as string) === 'gen3a_turbo') {
					durationOptions['duration'] = Property.StaticDropdown({
						displayName: 'Duration (seconds)',
						required: true,
						options: {
							options: [
								{ label: '5 seconds', value: 5 },
								{ label: '10 seconds', value: 10 },
							],
						},
					});
				} else {
					durationOptions['duration'] = Property.StaticDropdown({
						displayName: 'Duration (seconds)',
						required: true,
						options: {
							options: [
								{ label: 'Select a model first', value: '' },
							],
						},
					});
				}
				
				return durationOptions;
			},
		}),
		seed: Property.Number({ 
			displayName: 'Seed (Optional)', 
			description: 'Random seed for reproducible results (0-4294967295)',
			required: false 
		}),
		contentModeration: Property.DynamicProperties({
			auth: runwayAuth,
			displayName: 'Content Moderation',
			description: 'Content moderation settings (not available for veo3)',
			required: false,
			refreshers: ['model'],
			props: async ({ model }) => {
				const moderationOptions: DynamicPropsValue = {};
				
				if ((model as unknown as string) === 'veo3') {
					moderationOptions['info'] = Property.MarkDown({
						value: '**Note:** Content moderation is not supported by the veo3 model.',
					});
				} else {
					moderationOptions['publicFigureThreshold'] = Property.StaticDropdown({
						displayName: 'Public Figure Detection',
						description: 'How strict content moderation should be for recognizable public figures',
						required: false,
						defaultValue: 'auto',
						options: {
							options: [
								{ label: 'Auto (Recommended)', value: 'auto' },
								{ label: 'Low (Less Strict)', value: 'low' },
							],
						},
					});
				}
				
				return moderationOptions;
			},
		}),
	},
	async run({ auth, propsValue, files }) {
		// Zod validation
		await propsValidation.validateZod(propsValue, {
			promptText: z.optional(z.string().check(z.maxLength(1000, 'Prompt text must be 1000 characters or fewer'))),
			seed: z.optional(z.number().check(z.minimum(0, 'Seed must be at least 0'), z.maximum(4294967295, 'Seed must be at most 4294967295'))),
			promptImageUrl: z.optional(z.string().check(z.url('Prompt image URL must be a valid HTTPS URL'))),
		});

		// Input validation
		const hasFile = !!propsValue.promptImageFile;
		const hasUrl = !!propsValue.promptImageUrl;
		
		if ((hasFile && hasUrl) || (!hasFile && !hasUrl)) {
			throw new Error('You must provide either a Prompt Image File or a Prompt Image URL, but not both.');
		}

		// Model-specific validations
		const model = propsValue.model;
		const duration = (propsValue.duration as any)?.['duration'] || propsValue.duration;
		const ratio = (propsValue.ratio as any)?.['ratio'] || propsValue.ratio;
		const imagePosition = propsValue.imagePosition || 'first';
		
		// Validate duration per model
		if (model === 'veo3' && duration !== 8) {
			throw new Error('veo3 model requires a duration of exactly 8 seconds');
		}
		if ((model === 'gen4_turbo' || model === 'gen3a_turbo') && duration !== 5 && duration !== 10) {
			throw new Error(`${model} model requires a duration of either 5 or 10 seconds`);
		}

		// Validate ratio per model
		const validRatios = {
			gen4_turbo: ['1280:720', '720:1280', '1104:832', '832:1104', '960:960', '1584:672'],
			gen3a_turbo: ['1280:768', '768:1280'],
			veo3: ['1280:720', '720:1280']
		};
		
		if (!validRatios[model as keyof typeof validRatios]?.includes(ratio)) {
			throw new Error(`Invalid resolution ${ratio} for model ${model}`);
		}

		// Validate image position per model
		if (imagePosition === 'last' && model !== 'gen3a_turbo') {
			throw new Error('Last frame positioning is only supported by gen3a_turbo model');
		}

		// Prepare image URL
		let imageUrl: string;
		if (hasFile) {
			const f = propsValue.promptImageFile as any;
			const ext = f?.extension || 'jpeg';
			imageUrl = `data:image/${ext};base64,${f?.base64}`;
		} else {
			imageUrl = propsValue.promptImageUrl as string;
		}

		const apiKey = auth.secret_text;
		const client = new RunwayML({ apiKey });

		// Build request body according to SDK specification
		const requestBody: any = {
			model: model,
			ratio: ratio,
		};

		// Handle different image input formats based on position
		if (imagePosition === 'first' || model !== 'gen3a_turbo') {
			// Simple string format for first frame or non-gen3a_turbo models
			requestBody.promptImage = imageUrl;
		} else {
			// Array format for last frame positioning (gen3a_turbo only)
			requestBody.promptImage = [{
				uri: imageUrl,
				position: imagePosition
			}];
		}

		// Add optional parameters
		if (duration) {
			requestBody.duration = duration;
		}

		if (propsValue.promptText) {
			requestBody.promptText = propsValue.promptText;
		}

		if (propsValue.seed !== undefined) {
			requestBody.seed = propsValue.seed;
		}

		// Add content moderation only for supported models
		if (model !== 'veo3' && (propsValue.contentModeration as any)?.['publicFigureThreshold']) {
			requestBody.contentModeration = {
				publicFigureThreshold: (propsValue.contentModeration as any)['publicFigureThreshold']
			};
		}

		try {
			const task = await client.imageToVideo.create(requestBody);
			
			return {
				success: true,
				taskId: task.id,
			};
		} catch (error: any) {
			throw new Error(`Failed to generate video: ${error.message || 'Unknown error'}`);
		}
	},
});


