package gamemanagement

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

	gamev1 "hyapp.local/api/proto/game/v1"
)

func TestFetchBaishunGameSyncPlanFetchesSignsAndFormatsCatalog(t *testing.T) {
	const appKey = "baishun-test-key"
	var receivedPath string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		receivedPath = r.URL.Path
		var body map[string]any
		if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
			t.Fatalf("decode request: %v", err)
		}
		nonce := stringFromJSONValue(body["signature_nonce"])
		timestamp := stringFromJSONValue(body["timestamp"])
		if got, want := body["signature"], baishunMD5(nonce+appKey+timestamp); got != want {
			t.Fatalf("signature = %v, want %s body=%+v", got, want, body)
		}
		if body["app_channel"] != "test-channel" || stringFromJSONValue(body["app_id"]) != "21397507" || stringFromJSONValue(body["game_list_type"]) != "1" {
			t.Fatalf("request body mismatch: %+v", body)
		}
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"code":0,"message":"ok","data":[{"game_id":1006,"name":"Gold of Test","preview_url":"https://cdn.test/1006.png","download_url":"https://h5.test/1006/index.html","game_orientation":2,"safe_height":1024}]}`))
	}))
	defer server.Close()

	plan, err := fetchBaishunGameSyncPlan(t.Context(), &gamev1.GamePlatform{
		PlatformCode:   "baishun",
		AdapterType:    adapterBaishunV1,
		CallbackSecret: appKey,
		AdapterConfigJson: `{
			"app_id": 21397507,
			"app_channel": "test-channel",
			"provider_api_base_url": ` + strconv.Quote(server.URL) + `
		}`,
	}, syncGamesRequest{})
	if err != nil {
		t.Fatalf("fetchBaishunGameSyncPlan failed: %v", err)
	}
	if receivedPath != "/v1/api/gamelist" {
		t.Fatalf("path = %q, want /v1/api/gamelist", receivedPath)
	}
	if len(plan.Games) != 1 {
		t.Fatalf("games len = %d, want 1", len(plan.Games))
	}
	game := plan.Games[0]
	if game.GameID != "baishun_1006" || game.ProviderGameID != "1006" || game.GameName != "Gold of Test" {
		t.Fatalf("catalog identity mismatch: %+v", game)
	}
	if game.IconURL != "https://cdn.test/1006.png" || game.CoverURL != "https://cdn.test/1006.png" || game.Orientation != "landscape" || game.Status != "disabled" || game.LaunchMode != "full_screen" {
		t.Fatalf("catalog presentation mismatch: %+v", game)
	}
	if game.SafeHeight != 1024 {
		t.Fatalf("safeHeight = %d, want 1024", game.SafeHeight)
	}
	var merged struct {
		GameURLs map[string]string `json:"game_urls"`
	}
	if err := json.Unmarshal([]byte(mergeAdapterGameURLs(`{}`, selectedGameURLs(plan.GameURLs, plan.Games))), &merged); err != nil {
		t.Fatalf("decode merged config: %v", err)
	}
	if merged.GameURLs["1006"] != "https://h5.test/1006/index.html" {
		t.Fatalf("merged game url = %q", merged.GameURLs["1006"])
	}
}

func TestFetchLeaderCCGameSyncPlanReadsRemoteJSONList(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet || r.URL.Path != "/test_game_list.json" {
			t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
		}
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`[
			{"gameId":"9","name":"GREEDY BOX","title":"过关斩将","ver":11,"half_url":"https://games.test/greedy_half/index.html"},
			{"gameId":"7","name":"SuperRichGod","title":"超级财神","ver":11,"full_url":"https://games.test/rich/index.html"}
		]`))
	}))
	defer server.Close()

	plan, err := fetchProviderGameSyncPlan(t.Context(), &gamev1.GamePlatform{
		PlatformCode:      "lingxian",
		AdapterType:       adapterLeaderCCV1,
		AdapterConfigJson: `{"game_list_url":` + strconv.Quote(server.URL+"/test_game_list.json") + `}`,
	}, syncGamesRequest{})
	if err != nil {
		t.Fatalf("fetchProviderGameSyncPlan failed: %v", err)
	}
	if len(plan.Games) != 2 {
		t.Fatalf("games len = %d, want 2: %+v", len(plan.Games), plan.Games)
	}
	first, second := plan.Games[0], plan.Games[1]
	if first.GameID != "lingxian_9" || first.GameName != "GREEDY BOX" || first.LaunchMode != "half_screen" || first.Status != "disabled" {
		t.Fatalf("first game mismatch: %+v", first)
	}
	if second.GameID != "lingxian_7" || second.LaunchMode != "full_screen" {
		t.Fatalf("full URL fallback mismatch: %+v", second)
	}
	if plan.GameURLs["9"] != "https://games.test/greedy_half/index.html" || plan.GameURLs["7"] != "https://games.test/rich/index.html" {
		t.Fatalf("game URLs mismatch: %+v", plan.GameURLs)
	}
}

func TestFetchZeeOneGameSyncPlanReadsConfiguredGameURLs(t *testing.T) {
	plan, err := fetchProviderGameSyncPlan(t.Context(), &gamev1.GamePlatform{
		PlatformCode: "zeeone",
		AdapterType:  adapterZeeOneV1,
		AdapterConfigJson: `{
			"game_urls": {
				"1028": "https://dx3kkv99b7clg.cloudfront.net/test/1028_starShinePrincess/index.html",
				"1021": "https://dx3kkv99b7clg.cloudfront.net/test/1021_gold_of_olympus/index.html"
			}
		}`,
	}, syncGamesRequest{})
	if err != nil {
		t.Fatalf("fetchProviderGameSyncPlan failed: %v", err)
	}
	if len(plan.Games) != 2 {
		t.Fatalf("games len = %d, want 2", len(plan.Games))
	}
	first := plan.Games[0]
	if first.GameID != "zeeone_1021" || first.ProviderGameID != "1021" || first.GameName != "Gold of Olympus" {
		t.Fatalf("first game mismatch: %+v", first)
	}
	second := plan.Games[1]
	if second.GameID != "zeeone_1028" || second.ProviderGameID != "1028" || second.GameName != "Star Shine Princess" {
		t.Fatalf("second game mismatch: %+v", second)
	}
	if plan.GameURLs["1021"] != "https://dx3kkv99b7clg.cloudfront.net/test/1021_gold_of_olympus/index.html" {
		t.Fatalf("game url mismatch: %+v", plan.GameURLs)
	}
	if first.Status != "disabled" || first.LaunchMode != "full_screen" || first.Orientation != "portrait" {
		t.Fatalf("default fields mismatch: %+v", first)
	}
}

func TestFetchVivaGamesGameSyncPlanReadsConfiguredGameURLs(t *testing.T) {
	plan, err := fetchProviderGameSyncPlan(t.Context(), &gamev1.GamePlatform{
		PlatformCode: "vivagames",
		AdapterType:  adapterVivaGamesV1,
		AdapterConfigJson: `{
			"game_urls": {
				"2": "https://dev-rich2.s3.ap-southeast-1.amazonaws.com/richForever.html?game_id=2&isShow=1"
			},
			"game_names": {
				"2": "Rich Forever"
			}
		}`,
	}, syncGamesRequest{})
	if err != nil {
		t.Fatalf("fetchProviderGameSyncPlan failed: %v", err)
	}
	if len(plan.Games) != 1 {
		t.Fatalf("games len = %d, want 1", len(plan.Games))
	}
	game := plan.Games[0]
	if game.GameID != "vivagames_2" || game.ProviderGameID != "2" || game.GameName != "Rich Forever" {
		t.Fatalf("vivagames game mismatch: %+v", game)
	}
	if plan.GameURLs["2"] != "https://dev-rich2.s3.ap-southeast-1.amazonaws.com/richForever.html?game_id=2&isShow=1" {
		t.Fatalf("game url mismatch: %+v", plan.GameURLs)
	}
	if game.Status != "disabled" || game.LaunchMode != "full_screen" || game.Orientation != "portrait" {
		t.Fatalf("default fields mismatch: %+v", game)
	}
}

func TestFetchReyouGameSyncPlanReadsConfiguredGameURLs(t *testing.T) {
	plan, err := fetchProviderGameSyncPlan(t.Context(), &gamev1.GamePlatform{
		PlatformCode: "reyou",
		AdapterType:  adapterReyouV1,
		AdapterConfigJson: `{
			"game_urls": {
				"101": "https://games.reyou.example/test/box/half/?game_id=101"
			},
			"game_names": {
				"101": "Hot Game"
			}
		}`,
	}, syncGamesRequest{})
	if err != nil {
		t.Fatalf("fetchProviderGameSyncPlan failed: %v", err)
	}
	if len(plan.Games) != 1 {
		t.Fatalf("games len = %d, want 1", len(plan.Games))
	}
	game := plan.Games[0]
	if game.GameID != "reyou_101" || game.ProviderGameID != "101" || game.GameName != "Hot Game" {
		t.Fatalf("reyou game mismatch: %+v", game)
	}
	if plan.GameURLs["101"] != "https://games.reyou.example/test/box/half/?game_id=101" {
		t.Fatalf("game url mismatch: %+v", plan.GameURLs)
	}
	if game.Status != "disabled" || game.LaunchMode != "full_screen" || game.Orientation != "portrait" {
		t.Fatalf("default fields mismatch: %+v", game)
	}
}

func TestFetchZGameGameSyncPlanReadsConfiguredGameURLs(t *testing.T) {
	plan, err := fetchProviderGameSyncPlan(t.Context(), &gamev1.GamePlatform{
		PlatformCode: "zgame",
		AdapterType:  adapterZGameV1,
		AdapterConfigJson: `{
			"game_urls": {
				"1001": "https://zgame.example/h5/1001/index.html"
			},
			"game_names": {
				"1001": "ZGame Demo"
			}
		}`,
	}, syncGamesRequest{})
	if err != nil {
		t.Fatalf("fetchProviderGameSyncPlan failed: %v", err)
	}
	if len(plan.Games) != 1 {
		t.Fatalf("games len = %d, want 1", len(plan.Games))
	}
	game := plan.Games[0]
	if game.GameID != "zgame_1001" || game.ProviderGameID != "1001" || game.GameName != "ZGame Demo" {
		t.Fatalf("zgame game mismatch: %+v", game)
	}
	if plan.GameURLs["1001"] != "https://zgame.example/h5/1001/index.html" {
		t.Fatalf("game url mismatch: %+v", plan.GameURLs)
	}
	if game.Status != "disabled" || game.LaunchMode != "full_screen" || game.Orientation != "portrait" {
		t.Fatalf("default fields mismatch: %+v", game)
	}
	if len(game.Tags) < 2 || game.Tags[0] != "zgame" || game.Tags[1] != adapterZGameV1 {
		t.Fatalf("tags mismatch: %+v", game.Tags)
	}
}

func TestFetchAMGGameSyncPlanReadsConfiguredNumericGameURLs(t *testing.T) {
	plan, err := fetchProviderGameSyncPlan(t.Context(), &gamev1.GamePlatform{
		PlatformCode: "amg",
		AdapterType:  adapterAMGV1,
		AdapterConfigJson: `{
			"app_key":"kzphamrv01",
			"game_urls":{
				"1002":"https://dev.playamg.com/egyptslot/index.html",
				"1001":"https://dev.playamg.com/greedy/index.html",
				"not-a-game-id":"https://dev.playamg.com/ignored/index.html"
			},
			"game_names":{"1001":"Greedy","1002":"Egypt Slot"}
		}`,
	}, syncGamesRequest{})
	if err != nil {
		t.Fatalf("fetchProviderGameSyncPlan failed: %v", err)
	}
	if len(plan.Games) != 2 {
		t.Fatalf("games len = %d, want 2: %+v", len(plan.Games), plan.Games)
	}
	first := plan.Games[0]
	second := plan.Games[1]
	if first.GameID != "amg_1001" || first.ProviderGameID != "1001" || first.GameName != "Greedy" || second.ProviderGameID != "1002" {
		t.Fatalf("AMG catalog mismatch: first=%+v second=%+v", first, second)
	}
	if plan.GameURLs["1001"] != "https://dev.playamg.com/greedy/index.html" || len(first.Tags) < 2 || first.Tags[1] != adapterAMGV1 {
		t.Fatalf("AMG URL/tags mismatch: plan=%+v game=%+v", plan.GameURLs, first)
	}
}

func TestFetchYomiGameSyncPlanReadsConfiguredGameNames(t *testing.T) {
	plan, err := fetchProviderGameSyncPlan(t.Context(), &gamev1.GamePlatform{
		PlatformCode: "yomi",
		AdapterType:  adapterYomiV4,
		AdapterConfigJson: `{
			"game_names": {
				"481": "MahjongWays",
				"480": "ChickenPlinko"
			},
			"game_urls": {
				"481": "https://api-testv2.yomigame.games/gate/gateway/auth"
			}
		}`,
	}, syncGamesRequest{Status: "active"})
	if err != nil {
		t.Fatalf("fetchProviderGameSyncPlan failed: %v", err)
	}
	if len(plan.Games) != 2 {
		t.Fatalf("games len = %d, want 2", len(plan.Games))
	}
	first := plan.Games[0]
	if first.GameID != "yomi_480" || first.ProviderGameID != "480" || first.GameName != "ChickenPlinko" {
		t.Fatalf("first yomi game mismatch: %+v", first)
	}
	second := plan.Games[1]
	if second.GameID != "yomi_481" || second.ProviderGameID != "481" || second.GameName != "MahjongWays" {
		t.Fatalf("second yomi game mismatch: %+v", second)
	}
	if plan.GameURLs["481"] != "https://api-testv2.yomigame.games/gate/gateway/auth" {
		t.Fatalf("game url mismatch: %+v", plan.GameURLs)
	}
	if first.Status != "active" || first.LaunchMode != "full_screen" || first.Orientation != "portrait" {
		t.Fatalf("default fields mismatch: %+v", first)
	}
}

func TestSelectProviderGamesKeepsOnlySelectedIDs(t *testing.T) {
	games := []catalogRequest{
		{ProviderGameID: "1001", GameName: "one"},
		{ProviderGameID: "1002", GameName: "two"},
	}

	selected := selectProviderGames(games, []string{"1002"})

	if len(selected) != 1 || selected[0].ProviderGameID != "1002" {
		t.Fatalf("selected games mismatch: %+v", selected)
	}
}

func TestMissingBaishunSyncFieldsNamesExactInputs(t *testing.T) {
	missing := missingBaishunSyncFields(0, "", "", "")
	want := []string{
		"adapterConfigJson.app_id",
		"adapterConfigJson.app_channel",
		"回调密钥",
		"adapterConfigJson.game_list_url 或 provider_api_base_url",
	}
	if len(missing) != len(want) {
		t.Fatalf("missing len = %d, want %d: %+v", len(missing), len(want), missing)
	}
	for index := range want {
		if missing[index] != want[index] {
			t.Fatalf("missing[%d] = %q, want %q", index, missing[index], want[index])
		}
	}
}
