package integration

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"time"

	"google.golang.org/grpc"
	robotv1 "hyapp.local/api/proto/robot/v1"
	userv1 "hyapp.local/api/proto/user/v1"
	"hyapp/pkg/appcode"
)

const humanRobotPoolPageSize = int32(200)

// HumanRoomRobotPool 是真人房间机器人运行时按国家分组后的账号池。
type HumanRoomRobotPool struct {
	CountryCode  string
	RobotUserIDs []int64
}

// HumanRoomRobotPoolProvider 屏蔽 robot-service 和 user-service 的组合查询细节。
type HumanRoomRobotPoolProvider interface {
	ListHumanRoomRobotPools(ctx context.Context, appCode string) ([]HumanRoomRobotPool, error)
}

type GRPCHumanRoomRobotPoolProvider struct {
	robots robotv1.GameRobotServiceClient
	users  userv1.UserServiceClient
}

func NewGRPCHumanRoomRobotPoolProvider(robotConn grpc.ClientConnInterface, userConn grpc.ClientConnInterface) *GRPCHumanRoomRobotPoolProvider {
	return &GRPCHumanRoomRobotPoolProvider{
		robots: robotv1.NewGameRobotServiceClient(robotConn),
		users:  userv1.NewUserServiceClient(userConn),
	}
}

func (p *GRPCHumanRoomRobotPoolProvider) ListHumanRoomRobotPools(ctx context.Context, appCode string) ([]HumanRoomRobotPool, error) {
	if p == nil || p.robots == nil || p.users == nil {
		return nil, fmt.Errorf("human room robot pool provider is not configured")
	}
	app := appcode.Normalize(appCode)
	userIDs, err := p.listActiveRobotUserIDs(ctx, app)
	if err != nil {
		return nil, err
	}
	if len(userIDs) == 0 {
		return nil, nil
	}
	groups := make(map[string][]int64)
	for start := 0; start < len(userIDs); start += int(humanRobotPoolPageSize) {
		end := start + int(humanRobotPoolPageSize)
		if end > len(userIDs) {
			end = len(userIDs)
		}
		resp, err := p.users.BatchGetUsers(ctx, &userv1.BatchGetUsersRequest{
			Meta:    userRequestMeta(app, "room-human-robot-users"),
			UserIds: userIDs[start:end],
		})
		if err != nil {
			return nil, err
		}
		for _, userID := range userIDs[start:end] {
			user := resp.GetUsers()[userID]
			countryCode := normalizePoolCountryCode(user.GetCountry())
			if countryCode == "" {
				continue
			}
			groups[countryCode] = append(groups[countryCode], userID)
		}
	}
	pools := make([]HumanRoomRobotPool, 0, len(groups))
	for countryCode, ids := range groups {
		pools = append(pools, HumanRoomRobotPool{CountryCode: countryCode, RobotUserIDs: ids})
	}
	sort.Slice(pools, func(i, j int) bool { return pools[i].CountryCode < pools[j].CountryCode })
	return pools, nil
}

func (p *GRPCHumanRoomRobotPoolProvider) listActiveRobotUserIDs(ctx context.Context, app string) ([]int64, error) {
	userIDs := make([]int64, 0, humanRobotPoolPageSize)
	cursor := ""
	for {
		resp, err := p.robots.ListGameRobots(ctx, &robotv1.ListGameRobotsRequest{
			Meta:     robotRequestMeta(app, "room-human-robot-pool"),
			Status:   "active",
			PageSize: humanRobotPoolPageSize,
			Cursor:   cursor,
		})
		if err != nil {
			return nil, err
		}
		for _, robot := range resp.GetRobots() {
			if robot.GetUserId() > 0 {
				userIDs = append(userIDs, robot.GetUserId())
			}
		}
		cursor = strings.TrimSpace(resp.GetNextCursor())
		if cursor == "" {
			return uniquePositiveInt64(userIDs), nil
		}
	}
}

func robotRequestMeta(appCode string, requestID string) *robotv1.RequestMeta {
	return &robotv1.RequestMeta{
		RequestId: requestID,
		Caller:    "room-service",
		AppCode:   appcode.Normalize(appCode),
		SentAtMs:  time.Now().UTC().UnixMilli(),
	}
}

func userRequestMeta(appCode string, requestID string) *userv1.RequestMeta {
	return &userv1.RequestMeta{
		RequestId: requestID,
		Caller:    "room-service",
		AppCode:   appcode.Normalize(appCode),
		SentAtMs:  time.Now().UTC().UnixMilli(),
	}
}

func normalizePoolCountryCode(value string) string {
	return strings.ToUpper(strings.TrimSpace(value))
}

func uniquePositiveInt64(values []int64) []int64 {
	seen := make(map[int64]bool, len(values))
	out := make([]int64, 0, len(values))
	for _, value := range values {
		if value <= 0 || seen[value] {
			continue
		}
		seen[value] = true
		out = append(out, value)
	}
	return out
}
