package gamemanagement

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"

	"hyapp-admin-server/internal/integration/userclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/response"
	gamev1 "hyapp.local/api/proto/game/v1"

	"github.com/gin-gonic/gin"
)

func (h *Handler) ListSelfGames(c *gin.Context) {
	resp, err := h.game.ListSelfGames(c.Request.Context(), &gamev1.ListSelfGamesRequest{Meta: requestMeta(c)})
	if err != nil {
		response.ServerError(c, "获取自研游戏失败")
		return
	}
	items := make([]diceConfigDTO, 0, len(resp.GetGames()))
	for _, item := range resp.GetGames() {
		items = append(items, diceConfigFromProto(item))
	}
	response.OK(c, gin.H{"items": items, "serverTimeMs": resp.GetServerTimeMs()})
}

func (h *Handler) UpdateDiceConfig(c *gin.Context) {
	gameID := strings.TrimSpace(c.Param("game_id"))
	if gameID == "" {
		response.BadRequest(c, "游戏 ID 参数不正确")
		return
	}
	var req diceConfigRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "骰子配置参数不正确")
		return
	}
	resp, err := h.game.UpdateDiceConfig(c.Request.Context(), &gamev1.UpdateDiceConfigRequest{
		Meta:   requestMeta(c),
		Config: req.toProto(gameID),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := diceConfigFromProto(resp.GetConfig())
	h.auditLog(c, "update-dice-config", "game_self_game_configs", item.GameID, item.Status)
	response.OK(c, item)
}

func (h *Handler) GetSelfGameNewUserPolicy(c *gin.Context) {
	gameID := strings.TrimSpace(c.Param("game_id"))
	if gameID == "" {
		response.BadRequest(c, "游戏 ID 参数不正确")
		return
	}
	resp, err := h.game.GetSelfGameNewUserPolicy(c.Request.Context(), &gamev1.GetSelfGameNewUserPolicyRequest{
		Meta:   requestMeta(c),
		GameId: gameID,
	})
	if err != nil {
		response.ServerError(c, "获取新手保护策略失败")
		return
	}
	response.OK(c, selfGameNewUserPolicyFromProto(resp.GetPolicy()))
}

func (h *Handler) UpdateSelfGameNewUserPolicy(c *gin.Context) {
	gameID := strings.TrimSpace(c.Param("game_id"))
	if gameID == "" {
		response.BadRequest(c, "游戏 ID 参数不正确")
		return
	}
	var req selfGameNewUserPolicyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "新手保护策略参数不正确")
		return
	}
	resp, err := h.game.UpdateSelfGameNewUserPolicy(c.Request.Context(), &gamev1.UpdateSelfGameNewUserPolicyRequest{
		Meta:   requestMeta(c),
		Policy: req.toProto(gameID),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := selfGameNewUserPolicyFromProto(resp.GetPolicy())
	h.auditLog(c, "update-self-game-new-user-policy", "game_self_game_new_user_policies", item.GameID, item.StrategyLevel)
	response.OK(c, item)
}

func (h *Handler) ListSelfGameStakePools(c *gin.Context) {
	gameID := strings.TrimSpace(c.Param("game_id"))
	if gameID == "" {
		response.BadRequest(c, "游戏 ID 参数不正确")
		return
	}
	resp, err := h.game.ListSelfGameStakePools(c.Request.Context(), &gamev1.ListSelfGameStakePoolsRequest{
		Meta:   requestMeta(c),
		GameId: gameID,
	})
	if err != nil {
		response.ServerError(c, "获取档位奖池失败")
		return
	}
	items := make([]selfGameStakePoolDTO, 0, len(resp.GetPools()))
	for _, pool := range resp.GetPools() {
		items = append(items, selfGameStakePoolFromProto(pool))
	}
	response.OK(c, gin.H{"items": items, "serverTimeMs": resp.GetServerTimeMs()})
}

func (h *Handler) UpdateSelfGameStakePool(c *gin.Context) {
	gameID := strings.TrimSpace(c.Param("game_id"))
	stakeCoin, ok := parseStakeCoinParam(c)
	if gameID == "" || !ok {
		response.BadRequest(c, "档位奖池参数不正确")
		return
	}
	var req selfGameStakePoolRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "档位奖池配置参数不正确")
		return
	}
	resp, err := h.game.UpdateSelfGameStakePool(c.Request.Context(), &gamev1.UpdateSelfGameStakePoolRequest{
		Meta: requestMeta(c),
		Pool: req.toProto(gameID, stakeCoin),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := selfGameStakePoolFromProto(resp.GetPool())
	h.auditLog(c, "update-self-game-stake-pool", "game_self_game_stake_pools", fmt.Sprintf("%s:%d", gameID, stakeCoin), item.Status)
	response.OK(c, item)
}

func (h *Handler) AdjustSelfGameStakePool(c *gin.Context) {
	gameID := strings.TrimSpace(c.Param("game_id"))
	stakeCoin, ok := parseStakeCoinParam(c)
	if gameID == "" || !ok {
		response.BadRequest(c, "档位奖池参数不正确")
		return
	}
	var req dicePoolAdjustRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "档位奖池调整参数不正确")
		return
	}
	resp, err := h.game.AdjustSelfGameStakePool(c.Request.Context(), &gamev1.AdjustSelfGameStakePoolRequest{
		Meta:       requestMeta(c),
		GameId:     gameID,
		StakeCoin:  stakeCoin,
		AmountCoin: req.AmountCoin,
		Direction:  strings.TrimSpace(req.Direction),
		Reason:     strings.TrimSpace(req.Reason),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	h.auditLog(c, "adjust-self-game-stake-pool", "game_self_game_stake_pools", fmt.Sprintf("%s:%d", gameID, stakeCoin), fmt.Sprintf("%s:%d", req.Direction, req.AmountCoin))
	response.OK(c, gin.H{"adjustment": dicePoolAdjustmentFromProto(resp.GetAdjustment()), "config": diceConfigFromProto(resp.GetConfig())})
}

func parseStakeCoinParam(c *gin.Context) (int64, bool) {
	stakeCoin, err := strconv.ParseInt(strings.TrimSpace(c.Param("stake_coin")), 10, 64)
	if err != nil || stakeCoin <= 0 {
		return 0, false
	}
	return stakeCoin, true
}

func (h *Handler) ListDiceRobots(c *gin.Context) {
	pageSize := int32(parsePositiveInt(firstQuery(c, "pageSize", "page_size"), 50))
	requestID := middleware.CurrentRequestID(c)
	resp, err := h.game.ListDiceRobots(c.Request.Context(), &gamev1.ListDiceRobotsRequest{
		Meta:     requestMeta(c),
		GameId:   strings.TrimSpace(firstQuery(c, "gameId", "game_id")),
		Status:   strings.TrimSpace(c.Query("status")),
		PageSize: pageSize,
		Cursor:   strings.TrimSpace(c.Query("cursor")),
	})
	if err != nil {
		response.ServerError(c, "获取全站机器人失败")
		return
	}
	items := make([]diceRobotDTO, 0, len(resp.GetRobots()))
	for _, item := range resp.GetRobots() {
		items = append(items, diceRobotFromProto(item))
	}
	if err := h.enrichDiceRobotProfiles(c.Request.Context(), requestID, items); err != nil {
		response.ServerError(c, "获取机器人用户资料失败")
		return
	}
	response.OK(c, gin.H{"items": items, "nextCursor": resp.GetNextCursor(), "pageSize": pageSize, "serverTimeMs": resp.GetServerTimeMs()})
}

func (h *Handler) GenerateDiceRobots(c *gin.Context) {
	if h.user == nil {
		response.ServerError(c, "用户服务不可用")
		return
	}
	var req diceGenerateRobotsRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "机器人生成参数不正确")
		return
	}
	if req.Count <= 0 {
		req.Count = defaultRobotGenerateCount
	}
	if req.Count > maxRobotGenerateCount {
		req.Count = maxRobotGenerateCount
	}
	country := strings.TrimSpace(req.Country)
	if country == "" {
		country = "SA"
	}
	// likei 是优先资料源；测试服或本地未配置时用随机资料兜底，避免运维为了造机器人账号被外部历史库阻塞。
	profiles, err := robotProfilesForGenerate(c.Request.Context(), h.robotProfiles, int(req.Count))
	if err != nil {
		response.BadRequest(c, "获取机器人资料失败: "+err.Error())
		return
	}
	if len(profiles) < int(req.Count) {
		response.BadRequest(c, "机器人资料数量不足")
		return
	}
	userIDs := make([]int64, 0, req.Count)
	requestID := middleware.CurrentRequestID(c)
	meta := requestMeta(c)
	appCode := meta.GetAppCode()
	if appCode == "" {
		appCode = "lalu"
		meta.AppCode = appCode
	}
	actorUserID := int64(middleware.CurrentUserID(c))
	accountLanguage := robotAccountLanguage(req.NicknameLanguage)
	for index := int32(0); index < req.Count; index++ {
		profile := profiles[index]
		gender := strings.TrimSpace(req.Gender)
		if gender == "" {
			gender = strings.TrimSpace(profile.Gender)
		}
		if gender == "" {
			// likei 当前只提供昵称和头像；没有显式性别时保持旧的均匀分布，随机兜底资料会在 profile 中给出真实随机值。
			gender = robotGender(int(index), "")
		}
		created, err := h.user.QuickCreateAccount(c.Request.Context(), userclient.QuickCreateAccountRequest{
			RequestID:      requestID,
			Caller:         "admin-server",
			Password:       randomRobotPassword(),
			Username:       profile.Nickname,
			Avatar:         profile.Avatar,
			Gender:         gender,
			Country:        country,
			DeviceID:       "game-robot-" + randomRobotHex(8),
			Source:         "game_robot",
			InstallChannel: "admin",
			Platform:       "android",
			Language:       accountLanguage,
			Timezone:       "UTC",
		})
		if err != nil {
			response.BadRequest(c, "创建机器人用户失败: "+err.Error())
			return
		}
		if err := h.initializeGeneratedRobotAppearance(c.Request.Context(), requestID, appCode, actorUserID, created.UserID); err != nil {
			response.BadRequest(c, "初始化机器人装扮失败: "+err.Error())
			return
		}
		userIDs = append(userIDs, created.UserID)
	}
	resp, err := h.game.RegisterDiceRobots(c.Request.Context(), &gamev1.RegisterDiceRobotsRequest{
		Meta:    meta,
		GameId:  strings.TrimSpace(firstQuery(c, "gameId", "game_id")),
		UserIds: userIDs,
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	items := make([]diceRobotDTO, 0, len(resp.GetRobots()))
	for _, item := range resp.GetRobots() {
		items = append(items, diceRobotFromProto(item))
	}
	if err := h.enrichDiceRobotProfiles(c.Request.Context(), requestID, items); err != nil {
		response.ServerError(c, "获取机器人用户资料失败")
		return
	}
	h.auditLog(c, "generate-dice-robots", "game_self_game_robots", "universal", fmt.Sprintf("count=%d", len(userIDs)))
	response.OK(c, gin.H{"created": len(userIDs), "items": items})
}

func (h *Handler) SetDiceRobotStatus(c *gin.Context) {
	gameID := strings.TrimSpace(firstQuery(c, "gameId", "game_id"))
	if gameID == "" {
		gameID = "dice"
	}
	userID, err := parseInt64Param(c.Param("user_id"))
	if err != nil || userID <= 0 {
		response.BadRequest(c, "机器人用户 ID 参数不正确")
		return
	}
	var req diceRobotStatusRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "机器人状态参数不正确")
		return
	}
	resp, err := h.game.SetDiceRobotStatus(c.Request.Context(), &gamev1.SetDiceRobotStatusRequest{
		Meta:   requestMeta(c),
		GameId: gameID,
		UserId: userID,
		Status: strings.TrimSpace(req.Status),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := diceRobotFromProto(resp.GetRobot())
	h.auditLog(c, "set-dice-robot-status", "game_self_game_robots", item.UserID, item.Status)
	response.OK(c, item)
}

// DeleteDiceRobot 删除当前自研游戏的机器人登记，后台只移除可匹配机器人池，不触碰真实用户资料。
func (h *Handler) DeleteDiceRobot(c *gin.Context) {
	gameID := strings.TrimSpace(firstQuery(c, "gameId", "game_id"))
	if gameID == "" {
		gameID = "dice"
	}
	userID, err := parseInt64Param(c.Param("user_id"))
	if err != nil || userID <= 0 {
		response.BadRequest(c, "机器人用户 ID 参数不正确")
		return
	}
	// 后台删除只撤销 game-service 的机器人登记；真实用户仍保留在 user-service，历史对局和钱包流水可以继续按 user_id 追溯。
	if _, err := h.game.DeleteDiceRobot(c.Request.Context(), &gamev1.DeleteDiceRobotRequest{
		Meta:   requestMeta(c),
		GameId: gameID,
		UserId: userID,
	}); err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	h.auditLog(c, "delete-dice-robot", "game_self_game_robots", fmt.Sprintf("%d", userID), gameID)
	response.OK(c, gin.H{"deleted": true})
}

func randomRobotPassword() string {
	return "Robot#" + randomRobotHex(12)
}

func randomRobotHex(size int) string {
	buf := make([]byte, size)
	if _, err := rand.Read(buf); err != nil {
		return "fallback"
	}
	return hex.EncodeToString(buf)
}

func parseInt64Param(raw string) (int64, error) {
	var value int64
	_, err := fmt.Sscan(strings.TrimSpace(raw), &value)
	return value, err
}
