package gemini

import (
	"context"
	"fmt"
	"time"

	"google.golang.org/genai"
)

const (
	// Veo models
	ModelVeo31 = "veo-3.1-generate-preview"
	ModelVeo2  = "veo-2.0-generate-001"

	defaultVideoModel = ModelVeo31
)

// VideoRequest represents a video generation request
type VideoRequest struct {
	Model         string // Model to use (default: "veo-3.1-generate-preview")
	Prompt        string // Required: text description of the desired video
	Image         []byte // Optional: reference image for image-to-video
	ImageMimeType string // Optional: MIME type of the reference image (default: "image/png")
	AspectRatio   string // Optional: aspect ratio (e.g., "16:9", "9:16")
	Duration      string // Optional: video duration (e.g., "5s", "10s")
}

// VideoResponse represents a video generation response
type VideoResponse struct {
	Video VideoData // Generated video
}

// VideoData represents a single generated video
type VideoData struct {
	Data     []byte // Raw video bytes
	MimeType string // MIME type of the video
	URI      string // URI if available (for downloading)
}

// GenerateVideo generates a video using the Veo model
// Note: Video generation is asynchronous and this method polls until completion
func (c *Client) GenerateVideo(ctx context.Context, req VideoRequest) (*VideoResponse, error) {
	// Validate required fields
	if req.Prompt == "" {
		return nil, fmt.Errorf("%w: prompt is required", ErrInvalidConfig)
	}

	// Set defaults
	if req.Model == "" {
		req.Model = defaultVideoModel
	}

	c.logger.Debug("starting video generation",
		"model", req.Model,
		"prompt_length", len(req.Prompt),
		"has_image", len(req.Image) > 0,
	)

	// Build configuration
	var config *genai.GenerateVideosConfig
	if req.AspectRatio != "" || req.Duration != "" {
		config = &genai.GenerateVideosConfig{}
		if req.AspectRatio != "" {
			config.AspectRatio = req.AspectRatio
		}
		// Duration would be set here if the SDK supports it
	}

	// Prepare image input if provided
	var image *genai.Image
	if len(req.Image) > 0 {
		mimeType := req.ImageMimeType
		if mimeType == "" {
			mimeType = "image/png"
		}
		image = &genai.Image{
			ImageBytes: req.Image,
			MIMEType:   mimeType,
		}
	}

	// Start video generation (async operation) with retry
	var operation *genai.GenerateVideosOperation
	err := c.retryWithBackoff(ctx, "GenerateVideo", func() error {
		var apiErr error
		operation, apiErr = c.genaiClient.Models.GenerateVideos(ctx, req.Model, req.Prompt, image, config)
		if apiErr != nil {
			return classifyError(apiErr)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	c.logger.Debug("video generation started, polling for completion",
		"operation_name", operation.Name,
	)

	// Poll for completion
	startTime := time.Now()
	for !operation.Done {
		// Check timeout
		if time.Since(startTime) > c.config.VideoMaxWait {
			return nil, fmt.Errorf("%w: video generation timed out after %v", ErrTimeout, c.config.VideoMaxWait)
		}

		c.logger.Debug("waiting for video generation",
			"elapsed", time.Since(startTime).Round(time.Second),
		)

		// Wait before polling again
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		case <-time.After(c.config.VideoPollDelay):
		}

		// Get updated operation status with retry
		err = c.retryWithBackoff(ctx, "GetVideosOperation", func() error {
			var apiErr error
			operation, apiErr = c.genaiClient.Operations.GetVideosOperation(ctx, operation, nil)
			if apiErr != nil {
				return classifyError(apiErr)
			}
			return nil
		})
		if err != nil {
			return nil, err
		}
	}

	c.logger.Debug("video generation complete",
		"elapsed", time.Since(startTime).Round(time.Second),
	)

	// Check for errors in the operation
	if operation.Error != nil {
		msg, _ := operation.Error["message"].(string)
		code, _ := operation.Error["code"].(float64)
		return nil, fmt.Errorf("video generation failed: %s (code: %.0f)", msg, code)
	}
	if operation.Response == nil {
		return nil, fmt.Errorf("no video generated (nil response)")
	}
	if len(operation.Response.GeneratedVideos) == 0 {
		// Log the full response for debugging
		c.logger.Error("video generation returned empty",
			"response", fmt.Sprintf("%+v", operation.Response),
		)
		return nil, fmt.Errorf("no video generated (empty GeneratedVideos array)")
	}

	// Get the generated video
	generatedVideo := operation.Response.GeneratedVideos[0]
	if generatedVideo.Video == nil {
		return nil, fmt.Errorf("video data is empty")
	}

	videoResp := &VideoResponse{
		Video: VideoData{
			URI: generatedVideo.Video.URI,
		},
	}

	// Download the video if URI is provided
	if generatedVideo.Video.URI != "" {
		c.logger.Debug("downloading video",
			"uri", generatedVideo.Video.URI,
		)

		downloadResp, err := c.genaiClient.Files.Download(ctx, generatedVideo.Video, nil)
		if err != nil {
			// Return URI even if download fails
			c.logger.Warn("failed to download video, returning URI only",
				"error", err,
			)
			return videoResp, nil
		}

		videoResp.Video.Data = downloadResp
		videoResp.Video.MimeType = "video/mp4"
	}

	return videoResp, nil
}
