package gemini

import (
	"context"
	"crypto/rand"
	"encoding/binary"
	"fmt"

	"google.golang.org/genai"
)

const (
	// Gemini API seed range constraints
	minSeed = 1
	maxSeed = 999999
)

const (
	// Gemini native image models (Nano Banana Pro)
	ModelGemini3ProImage = "gemini-3-pro-image-preview"

	defaultImageModel = ModelGemini3ProImage
)

// ImageRequest represents an image generation request
type ImageRequest struct {
	Model       string // Model to use (default: "gemini-3-pro-image-preview")
	Prompt      string // Required: text description of the desired image
	AspectRatio string // Optional: aspect ratio (e.g., "16:9", "1:1", "9:16")
	Size        string // Optional: image size - "1K", "2K", "4K" (Gemini 3 Pro only)

	// Reference image for identity consistency
	ReferenceImage []byte // Optional: reference image bytes
	ReferenceMime  string // Optional: MIME type ("image/png", "image/webp")

	// Determinism controls
	Seed *int32 // Optional: seed for reproducible results (nil = random)
}

// ImageResponse represents an image generation response
type ImageResponse struct {
	Images []ImageData // List of generated images
	Text   string      // Optional text response from model
	Seed   *int32      // Seed used for generation
}

// ImageData represents a single generated image
type ImageData struct {
	Data     []byte // Raw image bytes
	MimeType string // MIME type of the image
}

// GenerateImage generates images using Gemini native image generation (Nano Banana Pro)
func (c *Client) GenerateImage(ctx context.Context, req ImageRequest) (*ImageResponse, error) {
	// Validate required fields
	if req.Prompt == "" {
		return nil, fmt.Errorf("%w: prompt is required", ErrInvalidConfig)
	}

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

	// Generate or validate provided seed
	var seed int32
	if req.Seed != nil {
		seed = *req.Seed
		// Validate seed is within Gemini's accepted range
		if seed < minSeed || seed > maxSeed {
			return nil, fmt.Errorf("%w: seed must be between %d and %d (got %d)",
				ErrInvalidConfig, minSeed, maxSeed, seed)
		}
	} else {
		// Generate cryptographically random seed
		var seedBytes [4]byte
		if _, err := rand.Read(seedBytes[:]); err != nil {
			return nil, fmt.Errorf("generate random seed: %w", err)
		}
		seed = int32(binary.LittleEndian.Uint32(seedBytes[:])%maxSeed) + minSeed
	}

	c.logger.Debug("generating image",
		"model", req.Model,
		"prompt_length", len(req.Prompt),
		"seed", seed,
		"has_reference", len(req.ReferenceImage) > 0,
	)

	// Build generation config
	config := &genai.GenerateContentConfig{
		ResponseModalities: []string{"image", "text"},
		Seed:               &seed,
	}

	// Apply image-specific config (aspect ratio, size)
	if req.AspectRatio != "" || req.Size != "" {
		config.ImageConfig = &genai.ImageConfig{}
		if req.AspectRatio != "" {
			config.ImageConfig.AspectRatio = req.AspectRatio
		}
		if req.Size != "" {
			config.ImageConfig.ImageSize = req.Size
		}
	}

	// Build content parts based on whether reference image is provided
	var parts []*genai.Part
	if len(req.ReferenceImage) > 0 {
		// Determine MIME type
		mime := req.ReferenceMime
		if mime == "" {
			mime = "image/png" // default
		}

		// Multipart content: reference image + text prompt
		parts = []*genai.Part{
			{InlineData: &genai.Blob{MIMEType: mime, Data: req.ReferenceImage}},
			{Text: req.Prompt},
		}
	} else {
		// Text-only content
		parts = []*genai.Part{
			{Text: req.Prompt},
		}
	}

	content := []*genai.Content{{Parts: parts}}

	// Call the API with retry for transient errors
	var response *genai.GenerateContentResponse
	err := c.retryWithBackoff(ctx, "GenerateImage", func() error {
		var apiErr error
		response, apiErr = c.genaiClient.Models.GenerateContent(ctx, req.Model, content, config)
		if apiErr != nil {
			return classifyError(apiErr)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	// Convert response
	imageResp := &ImageResponse{
		Images: make([]ImageData, 0),
		Seed:   &seed,
	}

	// Extract images and text from response
	if response != nil && len(response.Candidates) > 0 {
		candidate := response.Candidates[0]
		if candidate.Content != nil {
			for _, part := range candidate.Content.Parts {
				if part.Text != "" {
					imageResp.Text = part.Text
				}
				if part.InlineData != nil && len(part.InlineData.Data) > 0 {
					mimeType := "image/png"
					if part.InlineData.MIMEType != "" {
						mimeType = part.InlineData.MIMEType
					}
					imageResp.Images = append(imageResp.Images, ImageData{
						Data:     part.InlineData.Data,
						MimeType: mimeType,
					})
				}
			}
		}
	}

	if len(imageResp.Images) == 0 {
		return nil, fmt.Errorf("no images generated")
	}

	c.logger.Debug("image generation complete",
		"images_count", len(imageResp.Images),
	)

	return imageResp, nil
}
