package sandbox

import (
	"testing"

	"go.starlark.net/starlark"

	"chat-switchboard/providers"
)

// ─── ParseRequiresProvider ──────────────────

func TestParseRequiresProvider_Missing(t *testing.T) {
	_, ok := ParseRequiresProvider(map[string]any{})
	if ok {
		t.Error("missing requires_provider should return false")
	}
}

func TestParseRequiresProvider_BoolTrue(t *testing.T) {
	cfg, ok := ParseRequiresProvider(map[string]any{"requires_provider": true})
	if !ok {
		t.Fatal("boolean true should return ok=true")
	}
	if cfg.ProviderConfigID != "" || cfg.DefaultModel != "" {
		t.Errorf("boolean true should produce empty config, got %+v", cfg)
	}
}

func TestParseRequiresProvider_BoolFalse(t *testing.T) {
	_, ok := ParseRequiresProvider(map[string]any{"requires_provider": false})
	if ok {
		t.Error("boolean false should return ok=false")
	}
}

func TestParseRequiresProvider_ObjectFull(t *testing.T) {
	cfg, ok := ParseRequiresProvider(map[string]any{
		"requires_provider": map[string]any{
			"provider_config_id": "cfg-uuid-123",
			"model":              "claude-3-haiku",
		},
	})
	if !ok {
		t.Fatal("object form should return ok=true")
	}
	if cfg.ProviderConfigID != "cfg-uuid-123" {
		t.Errorf("provider_config_id = %q", cfg.ProviderConfigID)
	}
	if cfg.DefaultModel != "claude-3-haiku" {
		t.Errorf("model = %q", cfg.DefaultModel)
	}
}

func TestParseRequiresProvider_ObjectPartial(t *testing.T) {
	cfg, ok := ParseRequiresProvider(map[string]any{
		"requires_provider": map[string]any{
			"model": "gpt-4o-mini",
		},
	})
	if !ok {
		t.Fatal("should return ok=true")
	}
	if cfg.ProviderConfigID != "" {
		t.Errorf("provider_config_id should be empty, got %q", cfg.ProviderConfigID)
	}
	if cfg.DefaultModel != "gpt-4o-mini" {
		t.Errorf("model = %q", cfg.DefaultModel)
	}
}

func TestParseRequiresProvider_InvalidType(t *testing.T) {
	_, ok := ParseRequiresProvider(map[string]any{"requires_provider": "yes"})
	if ok {
		t.Error("string value should return false")
	}
}

// ─── starlarkToMessages ─────────────────────

func TestStarlarkToMessages_Valid(t *testing.T) {
	list := starlark.NewList(nil)

	msg1 := starlark.NewDict(2)
	_ = msg1.SetKey(starlark.String("role"), starlark.String("system"))
	_ = msg1.SetKey(starlark.String("content"), starlark.String("You are helpful."))
	list.Append(msg1)

	msg2 := starlark.NewDict(2)
	_ = msg2.SetKey(starlark.String("role"), starlark.String("user"))
	_ = msg2.SetKey(starlark.String("content"), starlark.String("Hello"))
	list.Append(msg2)

	msgs, err := starlarkToMessages(list)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(msgs) != 2 {
		t.Fatalf("expected 2 messages, got %d", len(msgs))
	}
	if msgs[0].Role != "system" || msgs[0].Content != "You are helpful." {
		t.Errorf("msg[0] = %+v", msgs[0])
	}
	if msgs[1].Role != "user" || msgs[1].Content != "Hello" {
		t.Errorf("msg[1] = %+v", msgs[1])
	}
}

func TestStarlarkToMessages_Empty(t *testing.T) {
	list := starlark.NewList(nil)
	msgs, err := starlarkToMessages(list)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(msgs) != 0 {
		t.Errorf("expected 0 messages, got %d", len(msgs))
	}
}

func TestStarlarkToMessages_Nil(t *testing.T) {
	msgs, err := starlarkToMessages(nil)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if msgs != nil {
		t.Errorf("expected nil, got %v", msgs)
	}
}

func TestStarlarkToMessages_MissingRole(t *testing.T) {
	list := starlark.NewList(nil)
	msg := starlark.NewDict(1)
	_ = msg.SetKey(starlark.String("content"), starlark.String("hello"))
	list.Append(msg)

	_, err := starlarkToMessages(list)
	if err == nil {
		t.Error("expected error for missing role")
	}
}

func TestStarlarkToMessages_MissingContent(t *testing.T) {
	list := starlark.NewList(nil)
	msg := starlark.NewDict(1)
	_ = msg.SetKey(starlark.String("role"), starlark.String("user"))
	list.Append(msg)

	_, err := starlarkToMessages(list)
	if err == nil {
		t.Error("expected error for missing content")
	}
}

func TestStarlarkToMessages_NonDictItem(t *testing.T) {
	list := starlark.NewList([]starlark.Value{starlark.String("not a dict")})

	_, err := starlarkToMessages(list)
	if err == nil {
		t.Error("expected error for non-dict item")
	}
}

func TestStarlarkToMessages_NonStringRole(t *testing.T) {
	list := starlark.NewList(nil)
	msg := starlark.NewDict(2)
	_ = msg.SetKey(starlark.String("role"), starlark.MakeInt(42))
	_ = msg.SetKey(starlark.String("content"), starlark.String("hello"))
	list.Append(msg)

	_, err := starlarkToMessages(list)
	if err == nil {
		t.Error("expected error for non-string role")
	}
}

// ─── completionToStarlark ───────────────────

func TestCompletionToStarlark(t *testing.T) {
	resp := &providers.CompletionResponse{
		Content:      "Hello! How can I help?",
		Model:        "claude-3-haiku-20240307",
		FinishReason: "stop",
		InputTokens:  15,
		OutputTokens: 8,
	}

	val, err := completionToStarlark(resp)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	d, ok := val.(*starlark.Dict)
	if !ok {
		t.Fatalf("expected dict, got %T", val)
	}

	// content
	cv, found, _ := d.Get(starlark.String("content"))
	if !found {
		t.Fatal("missing 'content'")
	}
	if s, _ := starlark.AsString(cv); s != "Hello! How can I help?" {
		t.Errorf("content = %q", s)
	}

	// model
	mv, found, _ := d.Get(starlark.String("model"))
	if !found {
		t.Fatal("missing 'model'")
	}
	if s, _ := starlark.AsString(mv); s != "claude-3-haiku-20240307" {
		t.Errorf("model = %q", s)
	}

	// finish_reason
	fv, found, _ := d.Get(starlark.String("finish_reason"))
	if !found {
		t.Fatal("missing 'finish_reason'")
	}
	if s, _ := starlark.AsString(fv); s != "stop" {
		t.Errorf("finish_reason = %q", s)
	}

	// input_tokens
	iv, found, _ := d.Get(starlark.String("input_tokens"))
	if !found {
		t.Fatal("missing 'input_tokens'")
	}
	if i, err := starlark.AsInt32(iv); err != nil || i != 15 {
		t.Errorf("input_tokens = %v (err=%v)", i, err)
	}

	// output_tokens
	ov, found, _ := d.Get(starlark.String("output_tokens"))
	if !found {
		t.Fatal("missing 'output_tokens'")
	}
	if i, err := starlark.AsInt32(ov); err != nil || i != 8 {
		t.Errorf("output_tokens = %v (err=%v)", i, err)
	}
}
