package app

import (
	"context"
	"encoding/json"
	"path/filepath"
	"testing"

	"sub2api-cn-relay-manager/internal/store/sqlite"
)

func TestAPIListPortalLogicalGroups(t *testing.T) {
	handler := NewAPIHandler("secret-token", ActionSet{
		ListPortalLogicalGroups: func(_ context.Context) ([]PortalLogicalGroupInfo, error) {
			return []PortalLogicalGroupInfo{{
				LogicalGroupID:   "gpt-shared",
				DisplayName:      "GPT Shared",
				UsageScenario:    "适合统一 GPT 产品入口",
				Recommendation:   "优先使用 gpt-5.4",
				NextStepHint:     "先创建测试 Key",
				VisibilityScope:  "login_required",
				PackageTier:      "pro",
				PurchaseCTALabel: "升级到 Pro",
				PurchaseCTAURL:   "https://sub.tksea.top/portal/upgrade/pro",
				Status:           "active",
				RouteCount:       2,
				ActiveRouteCount: 1,
				PublicModels: []PortalLogicalGroupModel{
					{PublicModel: "gpt-5.4", Status: "active"},
				},
			}}, nil
		},
	})

	request := httptestRequest(t, "GET", "/api/portal/logical-groups", nil, "")
	response := httptestRecorder(handler, request)
	assertStatusCode(t, response, 200)
	var listPayload struct {
		LogicalGroups []PortalLogicalGroupInfo `json:"logical_groups"`
	}
	if err := json.Unmarshal(response.Body().Bytes(), &listPayload); err != nil {
		t.Fatalf("json.Unmarshal() error = %v", err)
	}
	if len(listPayload.LogicalGroups) != 1 || listPayload.LogicalGroups[0].LogicalGroupID != "gpt-shared" || listPayload.LogicalGroups[0].RouteCount != 2 {
		t.Fatalf("portal logical groups payload = %+v", listPayload)
	}
	if listPayload.LogicalGroups[0].UsageScenario != "适合统一 GPT 产品入口" || listPayload.LogicalGroups[0].Recommendation != "优先使用 gpt-5.4" || listPayload.LogicalGroups[0].NextStepHint != "先创建测试 Key" {
		t.Fatalf("portal logical groups guidance = %+v", listPayload.LogicalGroups[0])
	}
	if listPayload.LogicalGroups[0].VisibilityScope != "login_required" || listPayload.LogicalGroups[0].PackageTier != "pro" || listPayload.LogicalGroups[0].PurchaseCTALabel != "升级到 Pro" || listPayload.LogicalGroups[0].PurchaseCTAURL != "https://sub.tksea.top/portal/upgrade/pro" {
		t.Fatalf("portal logical groups packaging = %+v", listPayload.LogicalGroups[0])
	}
}

func TestAPIGetPortalLogicalGroupModels(t *testing.T) {
	handler := NewAPIHandler("secret-token", ActionSet{
		ListPortalLogicalGroupModels: func(_ context.Context, logicalGroupID string) ([]PortalLogicalGroupModel, error) {
			if logicalGroupID != "gpt-shared" {
				t.Fatalf("logicalGroupID = %q, want gpt-shared", logicalGroupID)
			}
			return []PortalLogicalGroupModel{{PublicModel: "gpt-5.4", Status: "active"}}, nil
		},
	})

	request := httptestRequest(t, "GET", "/api/portal/logical-groups/gpt-shared/models", nil, "")
	response := httptestRecorder(handler, request)
	assertStatusCode(t, response, 200)
	var modelsPayload struct {
		Models []PortalLogicalGroupModel `json:"models"`
	}
	if err := json.Unmarshal(response.Body().Bytes(), &modelsPayload); err != nil {
		t.Fatalf("json.Unmarshal() error = %v", err)
	}
	if len(modelsPayload.Models) != 1 || modelsPayload.Models[0].PublicModel != "gpt-5.4" {
		t.Fatalf("portal models payload = %+v", modelsPayload)
	}
}

func TestNewActionSetPortalLogicalGroups(t *testing.T) {
	dbPath := filepath.Join(t.TempDir(), "portal-logical-groups.db")
	dsn := "file:" + filepath.ToSlash(dbPath) + "?_busy_timeout=5000"
	actions := NewActionSet(dsn)
	ctx := context.Background()

	store, err := sqlite.Open(ctx, dsn)
	if err != nil {
		t.Fatalf("sqlite.Open() error = %v", err)
	}
	defer store.Close()

	if _, err := store.LogicalGroups().Create(ctx, sqlite.LogicalGroup{
		LogicalGroupID:         "gpt-shared",
		DisplayName:            "GPT Shared",
		Status:                 "active",
		Description:            "Public GPT product",
		UsageScenario:          "适合统一 GPT 产品入口",
		Recommendation:         "优先使用 gpt-5.4",
		NextStepHint:           "先创建测试 Key",
		VisibilityScope:        "login_required",
		PackageTier:            "pro",
		PurchaseCTALabel:       "升级到 Pro",
		PurchaseCTAURL:         "https://sub.tksea.top/portal/upgrade/pro",
		RoutePolicy:            "priority",
		StickyMode:             "conversation_preferred",
		ConversationTTLSeconds: 7200,
		UserModelTTLSeconds:    1800,
		FailoverThreshold:      2,
		CooldownSeconds:        600,
	}); err != nil {
		t.Fatalf("LogicalGroups().Create(active) error = %v", err)
	}
	if _, err := store.LogicalGroups().Create(ctx, sqlite.LogicalGroup{
		LogicalGroupID: "disabled-group",
		DisplayName:    "Disabled Group",
		Status:         "disabled",
	}); err != nil {
		t.Fatalf("LogicalGroups().Create(disabled) error = %v", err)
	}
	if _, err := store.LogicalGroupModels().Create(ctx, sqlite.LogicalGroupModel{
		LogicalGroupID: "gpt-shared",
		PublicModel:    "gpt-5.4",
		Status:         "active",
	}); err != nil {
		t.Fatalf("LogicalGroupModels().Create(active) error = %v", err)
	}
	if _, err := store.LogicalGroupModels().Create(ctx, sqlite.LogicalGroupModel{
		LogicalGroupID: "gpt-shared",
		PublicModel:    "gpt-hidden",
		Status:         "disabled",
	}); err != nil {
		t.Fatalf("LogicalGroupModels().Create(disabled) error = %v", err)
	}
	if _, err := store.LogicalGroupRoutes().Create(ctx, sqlite.LogicalGroupRoute{
		RouteID:             "asxs",
		LogicalGroupID:      "gpt-shared",
		Name:                "ASXS",
		Status:              "active",
		Priority:            10,
		Weight:              100,
		ShadowGroupID:       "9",
		ShadowHostID:        "remote43",
		UpstreamBaseURLHint: "https://api.asxs.top/v1",
	}); err != nil {
		t.Fatalf("LogicalGroupRoutes().Create(active) error = %v", err)
	}
	if _, err := store.LogicalGroupRoutes().Create(ctx, sqlite.LogicalGroupRoute{
		RouteID:             "backup",
		LogicalGroupID:      "gpt-shared",
		Name:                "Backup",
		Status:              "cooldown",
		Priority:            20,
		Weight:              100,
		ShadowGroupID:       "9",
		ShadowHostID:        "remote43",
		UpstreamBaseURLHint: "https://backup.example.com",
	}); err != nil {
		t.Fatalf("LogicalGroupRoutes().Create(cooldown) error = %v", err)
	}

	listed, err := actions.ListPortalLogicalGroups(ctx)
	if err != nil {
		t.Fatalf("ListPortalLogicalGroups() error = %v", err)
	}
	if len(listed) != 1 || listed[0].LogicalGroupID != "gpt-shared" {
		t.Fatalf("ListPortalLogicalGroups() = %+v", listed)
	}
	if listed[0].RouteCount != 2 || listed[0].ActiveRouteCount != 1 {
		t.Fatalf("ListPortalLogicalGroups() route counts = %+v", listed[0])
	}
	if len(listed[0].PublicModels) != 1 || listed[0].PublicModels[0].PublicModel != "gpt-5.4" {
		t.Fatalf("ListPortalLogicalGroups() public models = %+v", listed[0].PublicModels)
	}

	group, err := actions.GetPortalLogicalGroup(ctx, "gpt-shared")
	if err != nil {
		t.Fatalf("GetPortalLogicalGroup() error = %v", err)
	}
	if group.DisplayName != "GPT Shared" || group.RoutePolicy != "priority" {
		t.Fatalf("GetPortalLogicalGroup() = %+v", group)
	}
	if group.UsageScenario != "适合统一 GPT 产品入口" || group.Recommendation != "优先使用 gpt-5.4" || group.NextStepHint != "先创建测试 Key" {
		t.Fatalf("GetPortalLogicalGroup() guidance = %+v", group)
	}
	if group.VisibilityScope != "login_required" || group.PackageTier != "pro" || group.PurchaseCTALabel != "升级到 Pro" || group.PurchaseCTAURL != "https://sub.tksea.top/portal/upgrade/pro" {
		t.Fatalf("GetPortalLogicalGroup() packaging = %+v", group)
	}

	models, err := actions.ListPortalLogicalGroupModels(ctx, "gpt-shared")
	if err != nil {
		t.Fatalf("ListPortalLogicalGroupModels() error = %v", err)
	}
	if len(models) != 1 || models[0].PublicModel != "gpt-5.4" {
		t.Fatalf("ListPortalLogicalGroupModels() = %+v", models)
	}

	request := httptestRequest(t, "GET", "/api/portal/logical-groups/gpt-shared", nil, "")
	response := httptestRecorder(NewAPIHandler("secret-token", actions), request)
	assertStatusCode(t, response, 200)
	var payload map[string]PortalLogicalGroupInfo
	if err := json.Unmarshal(response.Body().Bytes(), &payload); err != nil {
		t.Fatalf("json.Unmarshal() error = %v", err)
	}
	if payload["logical_group"].LogicalGroupID != "gpt-shared" {
		t.Fatalf("portal logical group payload = %+v", payload)
	}
	if payload["logical_group"].Recommendation != "优先使用 gpt-5.4" {
		t.Fatalf("portal logical group recommendation = %+v, want configured recommendation", payload["logical_group"])
	}
	if payload["logical_group"].PackageTier != "pro" {
		t.Fatalf("portal logical group package_tier = %+v, want configured package tier", payload["logical_group"])
	}
}
