package service

import (
	"encoding/json"
	"strconv"
	"testing"

	"github.com/alicebob/miniredis/v2"
	"github.com/ez-api/ez-api/internal/model"
	"github.com/redis/go-redis/v9"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func TestSyncProviders_WritesSnapshotAndRouting(t *testing.T) {
	mr := miniredis.RunT(t)
	rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
	svc := NewSyncService(rdb)

	db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
	if err != nil {
		t.Fatalf("open sqlite: %v", err)
	}
	if err := db.AutoMigrate(&model.ProviderGroup{}, &model.APIKey{}); err != nil {
		t.Fatalf("migrate: %v", err)
	}

	group := model.ProviderGroup{
		Name:           "default",
		Type:           "vertex-express",
		BaseURL:        "https://vertex.example",
		GoogleLocation: "global",
		Models:         "gemini-3-pro-preview",
		Status:         "active",
	}
	if err := db.Create(&group).Error; err != nil {
		t.Fatalf("create group: %v", err)
	}
	key := model.APIKey{
		GroupID: group.ID,
		APIKey:  "k",
		Status:  "active",
		AutoBan: true,
	}
	if err := db.Create(&key).Error; err != nil {
		t.Fatalf("create key: %v", err)
	}

	if err := svc.SyncProviders(db); err != nil {
		t.Fatalf("SyncProviders: %v", err)
	}

	raw := mr.HGet("config:providers", jsonID(key.ID))
	if raw == "" {
		t.Fatalf("expected config:providers hash entry")
	}
	var snap map[string]any
	if err := json.Unmarshal([]byte(raw), &snap); err != nil {
		t.Fatalf("invalid snapshot json: %v", err)
	}
	if snap["group"] != "default" {
		t.Fatalf("expected group default, got %#v", snap["group"])
	}

	routeKey := "route:group:default:gemini-3-pro-preview"
	if !mr.Exists(routeKey) {
		t.Fatalf("expected routing key %q to exist", routeKey)
	}
	ok, err := mr.SIsMember(routeKey, jsonID(key.ID))
	if err != nil {
		t.Fatalf("SIsMember: %v", err)
	}
	if !ok {
		t.Fatalf("expected provider id in routing set %q", routeKey)
	}
}

func TestSyncKey_WritesTokenID(t *testing.T) {
	mr := miniredis.RunT(t)
	rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
	svc := NewSyncService(rdb)

	k := &model.Key{
		TokenHash:        "hash",
		MasterID:         1,
		IssuedAtEpoch:    1,
		Status:           "active",
		Group:            "default",
		Scopes:           "chat:write",
		DefaultNamespace: "default",
		Namespaces:       "default",
	}
	k.ID = 123

	if err := svc.SyncKey(k); err != nil {
		t.Fatalf("SyncKey: %v", err)
	}

	if got := mr.HGet("auth:token:hash", "id"); got != "123" {
		t.Fatalf("expected auth:token:hash.id=123, got %q", got)
	}
}

func TestSyncModelDelete_RemovesMeta(t *testing.T) {
	mr := miniredis.RunT(t)
	rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
	svc := NewSyncService(rdb)

	mr.HSet("meta:models", "ns.m", `{"name":"ns.m"}`)

	m := &model.Model{Name: "ns.m"}
	if err := svc.SyncModelDelete(m); err != nil {
		t.Fatalf("SyncModelDelete: %v", err)
	}

	if got := mr.HGet("meta:models", "ns.m"); got != "" {
		t.Fatalf("expected meta:models entry removed, got %q", got)
	}
	if v := mr.HGet("meta:models_meta", "version"); v == "" {
		t.Fatalf("expected meta:models_meta.version to be set")
	}
}

func jsonID(id uint) string {
	return strconv.FormatUint(uint64(id), 10)
}
