package chat

import (
	"context"
	"errors"
	"testing"
	"time"
)

// fakeProvider is a minimal Provider for registry testing — no HTTP,
// just records what model name reached it.
type fakeProvider struct {
	name      string
	available bool
	got       Request // last request
}

func (f *fakeProvider) Name() string    { return f.name }
func (f *fakeProvider) Available() bool { return f.available }
func (f *fakeProvider) Chat(_ context.Context, req Request) (*Response, error) {
	f.got = req
	return &Response{Model: req.Model, Content: "ok from " + f.name}, nil
}

func newFake(name string, available bool) *fakeProvider {
	return &fakeProvider{name: name, available: available}
}

func TestRegistry_ResolveByPrefix(t *testing.T) {
	ollama := newFake("ollama", true)
	openrouter := newFake("openrouter", true)
	opencode := newFake("opencode", true)
	kimi := newFake("kimi", true)

	r := NewRegistry(ollama, openrouter, opencode, kimi)

	cases := []struct {
		model string
		want  string
	}{
		{"openrouter/anthropic/claude-opus-4-7", "openrouter"},
		{"opencode/claude-opus-4-7", "opencode"},
		{"kimi/kimi-for-coding", "kimi"},
		{"ollama/qwen3.5:latest", "ollama"},  // explicit prefix
		{"qwen3.5:latest", "ollama"},          // bare → default
		{"unknown/foo/bar", "ollama"},         // unknown prefix → default
	}
	for _, c := range cases {
		p, err := r.Resolve(c.model)
		if err != nil {
			t.Errorf("Resolve(%q): unexpected error: %v", c.model, err)
			continue
		}
		if p.Name() != c.want {
			t.Errorf("Resolve(%q) = %s, want %s", c.model, p.Name(), c.want)
		}
	}
}

func TestRegistry_ResolveCloudSuffix(t *testing.T) {
	ollama := newFake("ollama", true)
	cloud := newFake("ollama_cloud", true)
	r := NewRegistry(ollama, cloud)

	// :cloud suffix routes to ollama_cloud regardless of any prefix.
	p, err := r.Resolve("kimi-k2.6:cloud")
	if err != nil {
		t.Fatalf("Resolve kimi-k2.6:cloud: %v", err)
	}
	if p.Name() != "ollama_cloud" {
		t.Errorf("kimi-k2.6:cloud should route to ollama_cloud, got %s", p.Name())
	}

	// Without ollama_cloud registered, :cloud → ErrProviderNotFound
	// (don't silently fall through to local).
	rNoCloud := NewRegistry(ollama)
	if _, err := rNoCloud.Resolve("kimi-k2.6:cloud"); !errors.Is(err, ErrProviderNotFound) {
		t.Errorf("missing ollama_cloud should ErrProviderNotFound; got %v", err)
	}
}

func TestRegistry_ResolveErrors(t *testing.T) {
	r := NewRegistry()

	// Empty model
	if _, err := r.Resolve(""); !errors.Is(err, ErrProviderNotFound) {
		t.Errorf("empty model should ErrProviderNotFound; got %v", err)
	}

	// No providers registered, any model → 404
	if _, err := r.Resolve("openrouter/foo"); !errors.Is(err, ErrProviderNotFound) {
		t.Errorf("unregistered openrouter should 404; got %v", err)
	}
	if _, err := r.Resolve("bare-model"); !errors.Is(err, ErrProviderNotFound) {
		t.Errorf("bare with no default should 404; got %v", err)
	}
}

func TestRegistry_ChatStampsTelemetry(t *testing.T) {
	ollama := newFake("ollama", true)
	r := NewRegistry(ollama)

	resp, err := r.Chat(context.Background(), Request{Model: "qwen3.5:latest", Messages: []Message{{Role: "user", Content: "hi"}}})
	if err != nil {
		t.Fatalf("Chat: %v", err)
	}
	if resp.Provider != "ollama" {
		t.Errorf("Provider should be stamped to %q, got %q", "ollama", resp.Provider)
	}
	if resp.LatencyMs < 0 {
		t.Errorf("LatencyMs negative: %d", resp.LatencyMs)
	}
}

func TestRegistry_ChatProviderUnavailable(t *testing.T) {
	openrouter := newFake("openrouter", false) // no key
	r := NewRegistry(openrouter)

	_, err := r.Chat(context.Background(), Request{Model: "openrouter/foo"})
	if !errors.Is(err, ErrProviderDisabled) {
		t.Errorf("unavailable provider should ErrProviderDisabled; got %v", err)
	}
}

func TestStripPrefix(t *testing.T) {
	cases := []struct {
		model, prefix, want string
	}{
		{"openrouter/anthropic/claude", "openrouter", "anthropic/claude"},
		{"opencode/claude-opus-4-7", "opencode", "claude-opus-4-7"},
		{"qwen3.5:latest", "ollama", "qwen3.5:latest"},          // no prefix to strip
		{"ollama/qwen3.5:latest", "ollama", "qwen3.5:latest"},   // explicit ollama prefix
		{"kimi-k2.6:cloud", "cloud", "kimi-k2.6:cloud"},          // suffix doesn't trigger strip
	}
	for _, c := range cases {
		if got := StripPrefix(c.model, c.prefix); got != c.want {
			t.Errorf("StripPrefix(%q, %q) = %q, want %q", c.model, c.prefix, got, c.want)
		}
	}
}

func TestRegistry_Names(t *testing.T) {
	r := NewRegistry(
		newFake("zz", true),
		newFake("aa", true),
		newFake("mm", true),
	)
	names := r.Names()
	if len(names) != 3 || names[0] != "aa" || names[1] != "mm" || names[2] != "zz" {
		t.Errorf("Names() = %v, want sorted [aa mm zz]", names)
	}
}

// Time-stamp sanity — the dispatcher should never produce LatencyMs
// in the past.
func TestRegistry_LatencyMonotonic(t *testing.T) {
	ollama := newFake("ollama", true)
	r := NewRegistry(ollama)
	t0 := time.Now()
	resp, err := r.Chat(context.Background(), Request{Model: "qwen3.5:latest"})
	if err != nil {
		t.Fatalf("Chat: %v", err)
	}
	elapsed := time.Since(t0).Milliseconds()
	if resp.LatencyMs > elapsed+1 {
		t.Errorf("LatencyMs %d > elapsed %d (impossible)", resp.LatencyMs, elapsed)
	}
}
