package models

import (
	"testing"
)

func TestExtractOpenAIUsage(t *testing.T) {
	cases := []struct {
		name   string
		body   map[string]any
		want   *TokenUsage
		wantOK bool
	}{
		{
			name: "standard openai",
			body: map[string]any{
				"usage": map[string]any{
					"prompt_tokens":     float64(10),
					"completion_tokens": float64(20),
					"total_tokens":      float64(30),
				},
			},
			want:   &TokenUsage{PromptTokens: 10, CompletionTokens: 20, TotalTokens: 30},
			wantOK: true,
		},
		{
			name: "total missing falls back to sum",
			body: map[string]any{
				"usage": map[string]any{
					"prompt_tokens":     float64(10),
					"completion_tokens": float64(20),
				},
			},
			want:   &TokenUsage{PromptTokens: 10, CompletionTokens: 20, TotalTokens: 30},
			wantOK: true,
		},
		{
			name: "no usage key",
			body: map[string]any{
				"choices": []any{map[string]any{"message": map[string]any{"content": "hi"}}},
			},
			want:   nil,
			wantOK: false,
		},
		{
			name: "cohere style input_tokens/output_tokens",
			body: map[string]any{
				"usage": map[string]any{
					"input_tokens":  float64(100),
					"output_tokens": float64(50),
				},
			},
			want:   &TokenUsage{PromptTokens: 100, CompletionTokens: 50, TotalTokens: 150},
			wantOK: true,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got, ok := extractOpenAIUsage(tc.body)
			if ok != tc.wantOK {
				t.Fatalf("extractOpenAIUsage() ok = %v, want %v", ok, tc.wantOK)
			}
			if !tc.wantOK {
				return
			}
			if got.PromptTokens != tc.want.PromptTokens ||
				got.CompletionTokens != tc.want.CompletionTokens ||
				got.TotalTokens != tc.want.TotalTokens {
				t.Fatalf("extractOpenAIUsage() = %+v, want %+v", got, tc.want)
			}
		})
	}
}

func TestExtractOpenAIStreamUsage(t *testing.T) {
	cases := []struct {
		name   string
		event  map[string]any
		want   *TokenUsage
		wantOK bool
	}{
		{
			name: "standard streaming event",
			event: map[string]any{
				"usage": map[string]any{
					"prompt_tokens":     float64(100),
					"completion_tokens": float64(50),
					"total_tokens":      float64(150),
				},
			},
			want:   &TokenUsage{PromptTokens: 100, CompletionTokens: 50, TotalTokens: 150},
			wantOK: true,
		},
		{
			name: "null usage",
			event: map[string]any{
				"usage": nil,
			},
			want:   nil,
			wantOK: false,
		},
		{
			name: "no usage key",
			event: map[string]any{
				"choices": []any{map[string]any{"delta": map[string]any{"content": "hi"}}},
			},
			want:   nil,
			wantOK: false,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got, ok := extractOpenAIStreamUsage(tc.event)
			if ok != tc.wantOK {
				t.Fatalf("extractOpenAIStreamUsage() ok = %v, want %v", ok, tc.wantOK)
			}
			if !tc.wantOK {
				return
			}
			if got.PromptTokens != tc.want.PromptTokens ||
				got.CompletionTokens != tc.want.CompletionTokens ||
				got.TotalTokens != tc.want.TotalTokens {
				t.Fatalf("extractOpenAIStreamUsage() = %+v, want %+v", got, tc.want)
			}
		})
	}
}

func TestExtractToken(t *testing.T) {
	m := map[string]any{
		"a": float64(1),
		"b": int(2),
		"c": int64(3),
	}

	if got := extractToken(m, "a"); got != 1 {
		t.Fatalf("extractToken(a) = %d, want 1", got)
	}
	if got := extractToken(m, "b"); got != 2 {
		t.Fatalf("extractToken(b) = %d, want 2", got)
	}
	if got := extractToken(m, "c"); got != 3 {
		t.Fatalf("extractToken(c) = %d, want 3", got)
	}
	if got := extractToken(m, "missing"); got != 0 {
		t.Fatalf("extractToken(missing) = %d, want 0", got)
	}
	if got := extractToken(m, "missing", "a"); got != 1 {
		t.Fatalf("extractToken(missing, a) = %d, want 1", got)
	}
}
