package runway

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"image"
	"strings"
	"time"

	http "github.com/bogdanfinn/fhttp"
	tlsclient "github.com/bogdanfinn/tls-client"
	"github.com/google/uuid"
)

// GenerateImage runs the Runway "Nano Banana 2" (gemini_3_1_flash_image)
// text/image-to-image pipeline: upload each reference image (DATASET +
// DATASET_PREVIEW → dataset) to obtain its {assetId, url}, create a gemini image
// task and poll it to completion, then download the rendered PNG. teamID is the
// workspace id; if empty it's derived from the token. aspectRatio is passed
// through as-is (e.g. "16:9"); imageSize is the "1K"/"2K"/"4K" tier. refs may be
// empty (pure text-to-image).
func (c *Client) GenerateImage(ctx context.Context, token, teamID, prompt, aspectRatio, imageSize string, refs [][]byte) ([]byte, map[string]any, error) {
	token = strings.TrimSpace(strings.TrimPrefix(token, "Bearer "))
	if token == "" {
		return nil, nil, ErrAuth
	}
	if teamID == "" {
		teamID = TeamIDFromToken(token)
	}
	if teamID == "" {
		return nil, nil, errors.New("runway: no team id")
	}
	if strings.TrimSpace(aspectRatio) == "" {
		aspectRatio = "16:9"
	}
	if strings.TrimSpace(imageSize) == "" {
		imageSize = "1K"
	}

	client, err := c.newTLSClient()
	if err != nil {
		return nil, nil, err
	}

	var refImages []map[string]any
	for i, raw := range refs {
		if len(raw) == 0 {
			continue
		}
		filename := fmt.Sprintf("ref_%s_%d.png", time.Now().UTC().Format("20060102_150405"), i+1)
		assetID, url, upErr := c.uploadReference(ctx, client, token, teamID, filename, raw)
		if upErr != nil {
			return nil, nil, upErr
		}
		refImages = append(refImages, map[string]any{
			"tag":     fmt.Sprintf("IMG_%d", i+1),
			"assetId": assetID,
			"url":     url,
		})
	}

	taskID, err := c.createImageTask(ctx, client, token, teamID, prompt, aspectRatio, imageSize, refImages)
	if err != nil {
		return nil, nil, err
	}
	artifactURL, err := c.pollTask(ctx, client, token, teamID, taskID)
	if err != nil {
		return nil, nil, err
	}
	data, err := c.download(ctx, client, artifactURL)
	if err != nil {
		return nil, nil, err
	}
	meta := map[string]any{
		"provider":  "runway",
		"task_id":   taskID,
		"team_id":   teamID,
		"image_url": artifactURL,
	}
	return data, meta, nil
}

// uploadReference uploads one reference image through the dataset pipeline
// (DATASET_PREVIEW + DATASET uploads → /v1/datasets) and returns its asset id
// (= dataset id) and the cloudfront URL the task references.
func (c *Client) uploadReference(ctx context.Context, client tlsclient.HttpClient, token, teamID, filename string, data []byte) (string, string, error) {
	cfg, _, err := image.DecodeConfig(bytes.NewReader(data))
	if err != nil {
		return "", "", errors.New("runway: failed to decode reference image")
	}
	previewUploadID, _, err := c.uploadFile(ctx, client, token, teamID, filename, "DATASET_PREVIEW", data)
	if err != nil {
		return "", "", err
	}
	// The DATASET upload's completed URL is exactly what the task references.
	datasetUploadID, refURL, err := c.uploadFile(ctx, client, token, teamID, filename, "DATASET", data)
	if err != nil {
		return "", "", err
	}
	assetID, _, err := c.createDataset(ctx, client, token, teamID, filename, datasetUploadID, previewUploadID, cfg.Width, cfg.Height)
	if err != nil {
		return "", "", err
	}
	return assetID, refURL, nil
}

// createImageTask creates a gemini_3_1_flash_image task and returns its id.
func (c *Client) createImageTask(ctx context.Context, client tlsclient.HttpClient, token, teamID, prompt, aspectRatio, imageSize string, refImages []map[string]any) (string, error) {
	opts := map[string]any{
		"name":           "Nano Banana 2 - " + prompt,
		"text_prompt":    prompt,
		"aspect_ratio":   aspectRatio,
		"num_images":     1,
		"image_size":     imageSize,
		"model":          "gemini-3.1-flash-image-preview",
		"exploreMode":    false,
		"creationSource": "tool-mode",
	}
	if len(refImages) > 0 {
		opts["reference_images"] = refImages
	}
	res, err := c.apiJSON(ctx, client, token, teamID, http.MethodPost, "/v1/tasks", map[string]any{
		"taskType":  "gemini_3_1_flash_image",
		"options":   opts,
		"asTeamId":  jsonNumberOrString(teamID),
		"sessionId": uuid.NewString(),
	})
	if err != nil {
		return "", err
	}
	task, _ := res["task"].(map[string]any)
	id := strings.TrimSpace(stringValue(task["id"]))
	if id == "" {
		return "", fmt.Errorf("%w: image task missing id", ErrTemporaryUpstream)
	}
	return id, nil
}
