//go:build integration

package integration

import (
	"context"
	"encoding/base64"
	"testing"
	"time"

	"github.com/ollama/ollama/api"
)

func TestVisionModels(t *testing.T) {
	skipUnderMinVRAM(t, 6)
	type testCase struct {
		model string
	}
	testCases := []testCase{
		{
			model: "qwen2.5vl",
		},
		{
			model: "llama3.2-vision",
		},
		{
			model: "gemma3",
		},
		{
			model: "qwen3-vl:8b",
		},
		{
			// Qwen 3 VL mixture of experts
			model: "qwen3-vl:30b",
		},
		{
			model: "ministral-3",
		},
	}

	for _, v := range testCases {
		t.Run(v.model, func(t *testing.T) {
			image, err := base64.StdEncoding.DecodeString(imageEncoding)
			if err != nil {
				t.Fatal(err)
			}
			req := api.ChatRequest{
				Model: v.model,
				Messages: []api.Message{
					{
						Role:    "user",
						Content: "what does the text in this image say?",
						Images: []api.ImageData{
							image,
						},
					},
				},
				Stream: &stream,
				Options: map[string]any{
					"seed":        42,
					"temperature": 0.0,
				},
			}
			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
			defer cancel()
			client, _, cleanup := InitServerConnection(ctx, t)

			// Note: sometimes it returns "the ollamas" sometimes "the ollams"
			resp := "the ollam"
			defer cleanup()
			if err := PullIfMissing(ctx, client, req.Model); err != nil {
				t.Fatal(err)
			}
			// Preload to skip if we're less than 80% on GPU to avoid extremely slow tests
			err = client.Generate(ctx, &api.GenerateRequest{Model: req.Model}, func(response api.GenerateResponse) error { return nil })
			if err != nil {
				t.Fatalf("failed to load model %s: %s", req.Model, err)
			}
			skipIfNotGPULoaded(ctx, t, client, req.Model, 80)

			// llava models on CPU can be quite slow to start
			DoChat(ctx, t, client, req, []string{resp}, 240*time.Second, 30*time.Second)
		})
	}
}

func TestIntegrationSplitBatch(t *testing.T) {
	skipUnderMinVRAM(t, 6)
	image, err := base64.StdEncoding.DecodeString(imageEncoding)
	if err != nil {
		t.Fatal(err)
	}
	req := api.GenerateRequest{
		Model: "gemma3:4b",
		// Fill up a chunk of the batch so the image will partially spill over into the next one
		System: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed aliquet, justo in malesuada lobortis, odio ligula volutpat quam, quis faucibus ipsum magna quis sapien. Aliquam in venenatis diam, eu viverra magna. Phasellus imperdiet hendrerit volutpat. Vivamus sem ex, facilisis placerat felis non, dictum elementum est. Phasellus aliquam imperdiet lacus, eget placerat ligula sodales vel. Pellentesque nec auctor mi. Curabitur arcu nisi, faucibus eget nunc id, viverra interdum mi. Curabitur ornare ipsum ex, ac euismod ex aliquam in. Vestibulum id magna at purus accumsan fermentum. Proin scelerisque posuere nunc quis interdum. Maecenas sed mollis nisl. Etiam vitae ipsum interdum, placerat est quis, tincidunt velit. Nullam tempor nibh non lorem volutpat efficitur. Cras laoreet diam imperdiet ipsum auctor bibendum. Suspendisse ultrices urna sed metus sagittis suscipit. Quisque ullamcorper aliquam nibh ut mollis. Aenean dapibus mauris pharetra, venenatis elit ac, hendrerit odio. Cras vestibulum erat tempor, lobortis justo eu, lobortis ipsum. Nam laoreet dapibus sem. Proin vel diam ultrices, elementum ante et, ornare lectus. Proin eu accumsan nisl. Praesent ac ex vitae ipsum vulputate tristique facilisis sit amet lacus. Nullam faucibus magna a pellentesque pretium. Nunc lacinia ullamcorper sollicitudin. Donec vitae accumsan turpis, sed porttitor est. Donec porttitor mi vitae augue faucibus, vel mollis diam tincidunt.",
		Prompt: "what does the text in this image say?",
		Stream: &stream,
		Options: map[string]any{
			"seed":        42,
			"temperature": 0.0,
		},
		Images: []api.ImageData{
			image,
		},
	}

	// Note: sometimes it returns "the ollamas" sometimes "the ollams"
	resp := "the ollam"
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
	defer cancel()
	client, _, cleanup := InitServerConnection(ctx, t)
	defer cleanup()
	if err := PullIfMissing(ctx, client, req.Model); err != nil {
		t.Fatal(err)
	}
	// llava models on CPU can be quite slow to start,
	DoGenerate(ctx, t, client, req, []string{resp}, 120*time.Second, 30*time.Second)
}

const imageEncoding = `iVBORw0KGgoAAAANSUhEUgAAANIAAAB4CAYAAACHHqzKAAAAAXNSR0IArs4c6QAAAIRlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEb
[encoded data, 59774 characters]
voQTefWxZ9CMIP9/0Wj1ClQSO/4AAAAASUVORK5CYII=`
