package activityapi

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"hyapp/pkg/appcode"
	"hyapp/pkg/userleaderboard"
	"hyapp/services/gateway-service/internal/auth"

	roomv1 "hyapp.local/api/proto/room/v1"
)

func TestUserLeaderboardPeriodWindowUsesUTCBoundaries(t *testing.T) {
	now := time.Date(2026, 5, 20, 15, 30, 0, 0, time.FixedZone("CST", 8*60*60))

	todayStart, todayEnd := userLeaderboardPeriodWindow(leaderboardPeriodToday, now)
	if todayStart != time.Date(2026, 5, 20, 0, 0, 0, 0, time.UTC) {
		t.Fatalf("today start = %s", todayStart)
	}
	if !todayEnd.Equal(now.UTC()) {
		t.Fatalf("today end = %s", todayEnd)
	}

	weekStart, _ := userLeaderboardPeriodWindow(leaderboardPeriodWeek, now)
	if weekStart != time.Date(2026, 5, 18, 0, 0, 0, 0, time.UTC) {
		t.Fatalf("week start = %s", weekStart)
	}

	monthStart, _ := userLeaderboardPeriodWindow(leaderboardPeriodMonth, now)
	if monthStart != time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC) {
		t.Fatalf("month start = %s", monthStart)
	}
}

func TestNormalizeUserLeaderboardTypeAndPeriod(t *testing.T) {
	if normalizeUserLeaderboardType("receiver") != leaderboardTypeReceived {
		t.Fatal("receiver should normalize to received")
	}
	if normalizeUserLeaderboardType("rooms") != leaderboardTypeRoom {
		t.Fatal("rooms should normalize to room")
	}
	if normalizeUserLeaderboardPeriod("monthly") != leaderboardPeriodMonth {
		t.Fatal("monthly should normalize to month")
	}
}

func TestRoomLeaderboardRoomFromSnapshotIncludesAvatar(t *testing.T) {
	room := roomLeaderboardRoomFromSnapshot("room_1", &roomv1.RoomSnapshot{
		RoomId:      "room_1",
		RoomShortId: "1001",
		RoomExt: map[string]string{
			"title":     "Music Room",
			"cover_url": "https://cdn.example/room.png",
		},
	})

	if room.RoomID != "room_1" || room.RoomShortID != "1001" || room.Title != "Music Room" {
		t.Fatalf("room profile mismatch: %+v", room)
	}
	if room.CoverURL != "https://cdn.example/room.png" || room.RoomAvatar != room.CoverURL {
		t.Fatalf("room avatar fields mismatch: %+v", room)
	}
}

func TestUserLeaderboardItemsFromRoomGiftLeaderboardUsesRoomCard(t *testing.T) {
	items := userLeaderboardItemsFromRoomGiftLeaderboard([]*roomv1.RoomGiftLeaderboardItem{{
		Rank:      1,
		RoomId:    "room_1",
		CoinSpent: 900,
		Room: &roomv1.RoomListItem{
			RoomId:      "room_1",
			RoomShortId: "1001",
			Title:       "Music Room",
			CoverUrl:    "https://cdn.example/room.png",
		},
	}})

	if len(items) != 1 || items[0].RoomID != "room_1" || items[0].GiftValue != 900 {
		t.Fatalf("room leaderboard item mismatch: %+v", items)
	}
	if items[0].Room == nil || items[0].Room.RoomShortID != "1001" || items[0].Room.RoomAvatar != "https://cdn.example/room.png" {
		t.Fatalf("room leaderboard card mismatch: %+v", items[0].Room)
	}
}

func TestListUserLeaderboardsReadsRedisStore(t *testing.T) {
	store := &fakeUserLeaderboardStore{
		page: userleaderboard.Page{
			Items: []userleaderboard.Entry{{
				Rank:             1,
				UserID:           "20002",
				GiftValue:        900,
				GiftCount:        3,
				TransactionCount: 2,
				LastGiftAtMS:     1782461920000,
			}},
			Total:        1,
			BoardType:    leaderboardTypeSent,
			Period:       leaderboardPeriodWeek,
			StartAtMS:    1782172800000,
			EndAtMS:      1782461920000,
			ServerTimeMS: 1782461920000,
		},
		rank: userleaderboard.Entry{
			Rank:             9,
			UserID:           "10001",
			GiftValue:        300,
			GiftCount:        1,
			TransactionCount: 1,
			LastGiftAtMS:     1782460000000,
		},
		rankFound: true,
	}
	handler := &Handler{userLeaderboard: store}
	request := httptest.NewRequest(http.MethodGet, "/api/v1/activities/user-leaderboards?board_type=sent&period=week&page=1&page_size=20", nil)
	ctx := appcode.WithContext(request.Context(), "hyapp_room")
	ctx = auth.WithUserID(ctx, 10001)
	recorder := httptest.NewRecorder()

	handler.listUserLeaderboards(recorder, request.WithContext(ctx))

	if recorder.Code != http.StatusOK {
		t.Fatalf("status = %d body=%s", recorder.Code, recorder.Body.String())
	}
	if store.listCalls != 1 || store.rankCalls != 1 {
		t.Fatalf("store call count mismatch: list=%d rank=%d", store.listCalls, store.rankCalls)
	}
	if store.listQuery.AppCode != "hyapp_room" || store.listQuery.BoardType != leaderboardTypeSent || store.listQuery.Period != leaderboardPeriodWeek || store.listQuery.Page != 1 || store.listQuery.PageSize != 20 {
		t.Fatalf("list query mismatch: %+v", store.listQuery)
	}

	var envelope struct {
		Code string                  `json:"code"`
		Data userLeaderboardResponse `json:"data"`
	}
	if err := json.Unmarshal(recorder.Body.Bytes(), &envelope); err != nil {
		t.Fatalf("decode response failed: %v", err)
	}
	if envelope.Code != "OK" || envelope.Data.Total != 1 || len(envelope.Data.Items) != 1 {
		t.Fatalf("response mismatch: %+v", envelope)
	}
	if envelope.Data.Items[0].UserID != "20002" || envelope.Data.Items[0].GiftValue != 900 || envelope.Data.MyRank == nil || envelope.Data.MyRank.Rank != 9 {
		t.Fatalf("leaderboard data mismatch: %+v my=%+v", envelope.Data.Items, envelope.Data.MyRank)
	}
}

type fakeUserLeaderboardStore struct {
	listCalls int
	rankCalls int
	listQuery userleaderboard.Query
	rankQuery userleaderboard.Query
	page      userleaderboard.Page
	rank      userleaderboard.Entry
	rankFound bool
}

func (f *fakeUserLeaderboardStore) List(_ context.Context, query userleaderboard.Query) (userleaderboard.Page, error) {
	f.listCalls++
	f.listQuery = query
	return f.page, nil
}

func (f *fakeUserLeaderboardStore) RankForUser(_ context.Context, query userleaderboard.Query, userID int64) (userleaderboard.Entry, bool, error) {
	f.rankCalls++
	f.rankQuery = query
	if userID != 10001 {
		return userleaderboard.Entry{}, false, nil
	}
	return f.rank, f.rankFound, nil
}

type fakeUserLeaderboardRoomQueryClient struct {
	snapshotResp *roomv1.GetRoomSnapshotResponse
	snapshotErr  error
}

func (f *fakeUserLeaderboardRoomQueryClient) ListRooms(context.Context, *roomv1.ListRoomsRequest) (*roomv1.ListRoomsResponse, error) {
	return &roomv1.ListRoomsResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) ListRoomFeeds(context.Context, *roomv1.ListRoomFeedsRequest) (*roomv1.ListRoomsResponse, error) {
	return &roomv1.ListRoomsResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) ListRoomGiftLeaderboard(context.Context, *roomv1.ListRoomGiftLeaderboardRequest) (*roomv1.ListRoomGiftLeaderboardResponse, error) {
	return &roomv1.ListRoomGiftLeaderboardResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) GetMyRoom(context.Context, *roomv1.GetMyRoomRequest) (*roomv1.GetMyRoomResponse, error) {
	return &roomv1.GetMyRoomResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) GetCurrentRoom(context.Context, *roomv1.GetCurrentRoomRequest) (*roomv1.GetCurrentRoomResponse, error) {
	return &roomv1.GetCurrentRoomResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) GetRoomSnapshot(context.Context, *roomv1.GetRoomSnapshotRequest) (*roomv1.GetRoomSnapshotResponse, error) {
	if f.snapshotErr != nil {
		return nil, f.snapshotErr
	}
	if f.snapshotResp != nil {
		return f.snapshotResp, nil
	}
	return &roomv1.GetRoomSnapshotResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) GetRoomRocket(context.Context, *roomv1.GetRoomRocketRequest) (*roomv1.GetRoomRocketResponse, error) {
	return &roomv1.GetRoomRocketResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) ListRoomBackgrounds(context.Context, *roomv1.ListRoomBackgroundsRequest) (*roomv1.ListRoomBackgroundsResponse, error) {
	return &roomv1.ListRoomBackgroundsResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) ListRoomOnlineUsers(context.Context, *roomv1.ListRoomOnlineUsersRequest) (*roomv1.ListRoomOnlineUsersResponse, error) {
	return &roomv1.ListRoomOnlineUsersResponse{}, nil
}

func (f *fakeUserLeaderboardRoomQueryClient) ListRoomBannedUsers(context.Context, *roomv1.ListRoomBannedUsersRequest) (*roomv1.ListRoomBannedUsersResponse, error) {
	return &roomv1.ListRoomBannedUsersResponse{}, nil
}
