package application

import (
	"context"
	"strings"
	"testing"
)

type fakeOSSProvider struct {
	key         string
	data        []byte
	contentType string
	url         string
	deletedKey  string
}

func (f *fakeOSSProvider) Upload(_ context.Context, key string, data []byte, contentType string) (string, error) {
	f.key = key
	f.data = append([]byte(nil), data...)
	f.contentType = contentType
	if f.url == "" {
		return "https://cdn.example.com/" + key, nil
	}
	return f.url, nil
}

func (f *fakeOSSProvider) Name() string { return "fake" }

func (f *fakeOSSProvider) Delete(_ context.Context, key string) error {
	f.deletedKey = key
	return nil
}

type fakeSummarizer struct {
	prompt string
	url    string
	text   string
}

func (f *fakeSummarizer) SummarizeImage(_ context.Context, prompt string, imageURL string) (string, error) {
	f.prompt = prompt
	f.url = imageURL
	if f.text == "" {
		return "summary text", nil
	}
	return f.text, nil
}

func TestCaptureSummaryServiceUploadsSummarizesAndCopies(t *testing.T) {
	ctx := context.Background()
	provider := &fakeOSSProvider{}
	summarizer := &fakeSummarizer{}
	clip := &fakeClipboard{}
	svc := &CaptureSummaryService{
		Provider:   provider,
		Summarizer: summarizer,
		Clipboard:  clip,
		Prompt:     "describe screenshot",
		PathPrefix: "snapgo/",
	}

	uploaded, err := svc.UploadImage(ctx, []byte("png"))
	if err != nil {
		t.Fatalf("upload image: %v", err)
	}
	if provider.contentType != "image/png" {
		t.Fatalf("expected image/png content type, got %q", provider.contentType)
	}
	if string(provider.data) != "png" {
		t.Fatalf("expected uploaded png bytes, got %q", string(provider.data))
	}

	summary, err := svc.Summarize(ctx, uploaded.URL)
	if err != nil {
		t.Fatalf("summarize: %v", err)
	}
	if summarizer.prompt != "describe screenshot" {
		t.Fatalf("expected prompt forwarded, got %q", summarizer.prompt)
	}
	if summarizer.url != uploaded.URL {
		t.Fatalf("expected uploaded url forwarded, got %q", summarizer.url)
	}
	if summary != "summary text" {
		t.Fatalf("expected summary text, got %q", summary)
	}

	if err := svc.CopySummary(ctx, summary); err != nil {
		t.Fatalf("copy summary: %v", err)
	}
	if clip.text != "summary text" {
		t.Fatalf("expected summary copied, got %q", clip.text)
	}
}

func TestCaptureSummaryServiceInlineDataURL(t *testing.T) {
	svc := &CaptureSummaryService{MaxInlineBytes: 10}

	if !svc.CanInline([]byte("small")) {
		t.Fatalf("expected 5-byte payload to be inlinable under 10-byte cap")
	}
	if svc.CanInline([]byte("this is definitely too big")) {
		t.Fatalf("expected oversized payload to be rejected for inlining")
	}

	url := svc.InlineDataURL([]byte("png"))
	if !strings.HasPrefix(url, "data:image/png;base64,") {
		t.Fatalf("expected base64 data URL prefix, got %q", url)
	}
	if strings.Contains(url, "https://") {
		t.Fatalf("inline URL must not be a remote URL, got %q", url)
	}
}

func TestCaptureSummaryServiceDeleteUploadedUsesDeleter(t *testing.T) {
	provider := &fakeOSSProvider{}
	svc := &CaptureSummaryService{Provider: provider}

	if err := svc.DeleteUploaded(context.Background(), "snapgo/a.png"); err != nil {
		t.Fatalf("delete uploaded: %v", err)
	}
	if provider.deletedKey != "snapgo/a.png" {
		t.Fatalf("expected object deleted, got %q", provider.deletedKey)
	}
}

func TestCaptureSummaryServiceDeleteUploadedNoDeleterIsNoop(t *testing.T) {
	// A provider without Delete must not error when cleanup is requested.
	svc := &CaptureSummaryService{Provider: nonDeletingProvider{}}
	if err := svc.DeleteUploaded(context.Background(), "snapgo/a.png"); err != nil {
		t.Fatalf("expected no-op delete, got %v", err)
	}
}

type nonDeletingProvider struct{}

func (nonDeletingProvider) Upload(_ context.Context, _ string, _ []byte, _ string) (string, error) {
	return "https://cdn.example.com/x.png", nil
}

func (nonDeletingProvider) Name() string { return "no-delete" }
