package game

import (
	"bindbox-game/internal/code"
	"bindbox-game/internal/pkg/core"
	"bindbox-game/internal/pkg/logger"
	"bindbox-game/internal/pkg/validation"
	"bindbox-game/internal/repository/mysql"
	"bindbox-game/internal/repository/mysql/dao"
	"bindbox-game/internal/service/game"
	usersvc "bindbox-game/internal/service/user"
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
)

type handler struct {
	logger       logger.CustomLogger
	db           mysql.Repo
	redis        *redis.Client
	ticketSvc    game.TicketService
	gameTokenSvc game.GameTokenService
	userSvc      usersvc.Service
	readDB       *dao.Query
}

func New(l logger.CustomLogger, db mysql.Repo, rdb *redis.Client, userSvc usersvc.Service) *handler {
	return &handler{
		logger:       l,
		db:           db,
		redis:        rdb,
		ticketSvc:    game.NewTicketService(l, db),
		gameTokenSvc: game.NewGameTokenService(l, db, rdb),
		userSvc:      userSvc,
		readDB:       dao.Use(db.GetDbR()),
	}
}

// ========== Admin API ==========

type grantTicketRequest struct {
	GameCode string `json:"game_code" binding:"required"`
	Amount   int    `json:"amount" binding:"required,min=1"`
	Remark   string `json:"remark"`
}

// GrantUserTicket Admin为用户发放游戏资格
// @Summary 发放游戏资格
// @Tags 管理端.游戏
// @Param user_id path int true "用户ID"
// @Param RequestBody body grantTicketRequest true "请求参数"
// @Success 200 {object} map[string]any
// @Router /api/admin/users/{user_id}/game_tickets [post]
func (h *handler) GrantUserTicket() core.HandlerFunc {
	return func(ctx core.Context) {
		userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
		if err != nil || userID <= 0 {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "invalid user_id"))
			return
		}

		req := new(grantTicketRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}

		err = h.ticketSvc.GrantTicket(ctx.RequestContext(), userID, req.GameCode, req.Amount, "admin", 0, req.Remark)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error()))
			return
		}

		ctx.Payload(map[string]any{"success": true})
	}
}

// ListUserTickets Admin查询用户游戏资格日志
// @Summary 查询用户游戏资格日志
// @Tags 管理端.游戏
// @Param user_id path int true "用户ID"
// @Param page query int false "页码"
// @Param page_size query int false "每页数量"
// @Success 200 {object} map[string]any
// @Router /api/admin/users/{user_id}/game_tickets [get]
func (h *handler) ListUserTickets() core.HandlerFunc {
	return func(ctx core.Context) {
		userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
		if err != nil || userID <= 0 {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "invalid user_id"))
			return
		}

		var req struct {
			Page     int `form:"page"`
			PageSize int `form:"page_size"`
		}
		_ = ctx.ShouldBindQuery(&req)

		if req.Page <= 0 {
			req.Page = 1
		}
		if req.PageSize <= 0 {
			req.PageSize = 20
		}

		logs, total, err := h.ticketSvc.GetTicketLogs(ctx.RequestContext(), userID, req.Page, req.PageSize)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error()))
			return
		}

		ctx.Payload(map[string]any{
			"list":      logs,
			"total":     total,
			"page":      req.Page,
			"page_size": req.PageSize,
		})
	}
}

// ========== App API ==========

// GetMyTickets App获取我的游戏资格
// @Summary 获取我的游戏资格
// @Tags APP端.游戏
// @Param user_id path int true "用户ID"
// @Success 200 {object} map[string]int
// @Router /api/app/users/{user_id}/game_tickets [get]
func (h *handler) GetMyTickets() core.HandlerFunc {
	return func(ctx core.Context) {
		userID := int64(ctx.SessionUserInfo().Id)

		tickets, err := h.ticketSvc.GetUserTickets(ctx.RequestContext(), userID)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.ServerError, err.Error()))
			return
		}

		ctx.Payload(tickets)
	}
}

type enterGameRequest struct {
	GameCode string `json:"game_code" binding:"required"`
}

type enterGameResponse struct {
	GameToken      string `json:"game_token"`
	ExpiresAt      string `json:"expires_at"`
	NakamaServer   string `json:"nakama_server"`
	NakamaKey      string `json:"nakama_key"`
	RemainingTimes int    `json:"remaining_times"`
	ClientUrl      string `json:"client_url"`
}

// EnterGame App进入游戏(消耗资格)
// @Summary 进入游戏
// @Tags APP端.游戏
// @Param RequestBody body enterGameRequest true "请求参数"
// @Success 200 {object} enterGameResponse
// @Router /api/app/games/enter [post]
func (h *handler) EnterGame() core.HandlerFunc {
	return func(ctx core.Context) {
		sessionInfo := ctx.SessionUserInfo()
		userID := int64(sessionInfo.Id)
		username := sessionInfo.NickName
		avatar := "" // Avatar not in session, could be fetched from user profile if needed

		req := new(enterGameRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}

		// 生成安全的 GameToken (会自动扣减游戏次数)
		gameToken, _, expiresAt, err := h.gameTokenSvc.GenerateToken(
			ctx.RequestContext(),
			userID,
			username,
			avatar,
			req.GameCode,
		)
		if err != nil {
			h.logger.Error("Failed to generate game token", zap.Error(err))
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 180001, "游戏次数不足或生成Token失败"))
			return
		}

		// 查询剩余次数
		ticket, _ := h.ticketSvc.GetUserTicketByGame(ctx.RequestContext(), userID, req.GameCode)
		remaining := 0
		if ticket != nil {
			remaining = int(ticket.Available)
		}

		// 从系统配置读取Nakama服务器信息
		nakamaServer := "wss://nakama.yourdomain.com"
		nakamaKey := "defaultkey"
		clientUrl := "https://game.1024tool.vip"
		configKey := "game_" + req.GameCode + "_config"
		// map generic game code to specific config key if needed, or just use convention
		if req.GameCode == "minesweeper" {
			configKey = "game_minesweeper_config"
		}

		conf, _ := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq(configKey)).First()
		if conf != nil {
			var gameConfig struct {
				Server    string `json:"server"`
				Key       string `json:"key"`
				ClientUrl string `json:"client_url"`
			}
			if json.Unmarshal([]byte(conf.ConfigValue), &gameConfig) == nil {
				if gameConfig.Server != "" {
					nakamaServer = gameConfig.Server
				}
				if gameConfig.Key != "" {
					nakamaKey = gameConfig.Key
				}
				if gameConfig.ClientUrl != "" {
					clientUrl = gameConfig.ClientUrl
				}
			}
		}

		ctx.Payload(&enterGameResponse{
			GameToken:      gameToken,
			ExpiresAt:      expiresAt.Format("2006-01-02T15:04:05Z07:00"),
			NakamaServer:   nakamaServer,
			NakamaKey:      nakamaKey,
			RemainingTimes: remaining,
			ClientUrl:      clientUrl,
		})
	}
}

// ========== Internal API (Nakama调用) ==========

type validateTokenRequest struct {
	GameToken string `json:"game_token" binding:"required"`
}

type validateTokenResponse struct {
	Valid    bool   `json:"valid"`
	UserID   int64  `json:"user_id,omitempty"`
	Username string `json:"username,omitempty"`
	Avatar   string `json:"avatar,omitempty"`
	GameType string `json:"game_type,omitempty"`
	Ticket   string `json:"ticket,omitempty"`
	Error    string `json:"error,omitempty"`
}

// ValidateGameToken Internal验证GameToken
// @Summary 验证GameToken
// @Tags Internal.游戏
// @Param RequestBody body validateTokenRequest true "请求参数"
// @Success 200 {object} validateTokenResponse
// @Router /internal/game/validate-token [post]
func (h *handler) ValidateGameToken() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(validateTokenRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.Payload(&validateTokenResponse{Valid: false, Error: "invalid request"})
			return
		}

		claims, err := h.gameTokenSvc.ValidateToken(ctx.RequestContext(), req.GameToken)
		if err != nil {
			h.logger.Warn("GameToken validation failed", zap.Error(err))
			ctx.Payload(&validateTokenResponse{Valid: false, Error: err.Error()})
			return
		}

		ctx.Payload(&validateTokenResponse{
			Valid:    true,
			UserID:   claims.UserID,
			Username: claims.Username,
			Avatar:   claims.Avatar,
			GameType: claims.GameType,
			Ticket:   claims.Ticket,
		})
	}
}

type verifyRequest struct {
	UserID string `json:"user_id"`
	Ticket string `json:"ticket"`
}

type verifyResponse struct {
	Valid      bool           `json:"valid"`
	UserID     string         `json:"user_id"`
	GameConfig map[string]any `json:"game_config,omitempty"`
}

// VerifyTicket Internal验证游戏票据
// @Summary 验证票据
// @Tags Internal.游戏
// @Param RequestBody body verifyRequest true "请求参数"
// @Success 200 {object} verifyResponse
// @Router /internal/game/verify [post]
func (h *handler) VerifyTicket() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(verifyRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}

		// 从Redis验证token
		storedUserID, err := h.redis.Get(ctx.RequestContext(), "game:token:ticket:"+req.Ticket).Result()
		if err != nil || storedUserID != req.UserID {
			ctx.Payload(&verifyResponse{Valid: false})
			return
		}

		// 获取游戏配置
		gameConfig := make(map[string]any)
		configKey := "game_minesweeper_config"
		conf, err := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq(configKey)).First()
		if err == nil && conf != nil {
			json.Unmarshal([]byte(conf.ConfigValue), &gameConfig)
		}

		ctx.Payload(&verifyResponse{Valid: true, UserID: req.UserID, GameConfig: gameConfig})
	}
}

type settleRequest struct {
	UserID  string `json:"user_id"`
	Ticket  string `json:"ticket"`
	MatchID string `json:"match_id"`
	Win     bool   `json:"win"`
	Score   int    `json:"score"`
}

type settleResponse struct {
	Success bool   `json:"success"`
	Reward  string `json:"reward,omitempty"`
}

// SettleGame Internal游戏结算
// @Summary 游戏结算
// @Tags Internal.游戏
// @Param RequestBody body settleRequest true "请求参数"
// @Success 200 {object} settleResponse
// @Router /internal/game/settle [post]
func (h *handler) SettleGame() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(settleRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}

		// 验证token（可选，如果游戏服务器传了ticket则验证，否则信任internal调用）
		if req.Ticket != "" {
			storedUserID, err := h.redis.Get(ctx.RequestContext(), "game:token:ticket:"+req.Ticket).Result()
			if err != nil || storedUserID != req.UserID {
				h.logger.Warn("Ticket validation failed, but proceeding with internal trust",
					zap.String("ticket", req.Ticket), zap.String("user_id", req.UserID))
			} else {
				// 删除token防止重复使用
				h.redis.Del(ctx.RequestContext(), "game:token:ticket:"+req.Ticket)
			}
		}
		// 注意：即使ticket验证失败，作为internal API我们仍然信任游戏服务器传来的UserID

		// 奖品发放逻辑
		var rewardMsg string
		var msConfig struct {
			WinnerRewardPoints           int64 `json:"winner_reward_points"`
			ParticipationRewardPoints    int64 `json:"participation_reward_points"`
			WinnerRewardProductID        int64 `json:"winner_reward_product_id"`
			ParticipationRewardProductID int64 `json:"participation_reward_product_id"`
		}

		// 1. 读取配置
		configKey := "game_minesweeper_config"
		conf, err := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq(configKey)).First()
		if err == nil && conf != nil {
			json.Unmarshal([]byte(conf.ConfigValue), &msConfig)
		}

		uid, _ := strconv.ParseInt(req.UserID, 10, 64)

		// 2. 确定奖励内容
		var targetProductID int64
		var targetPoints int64

		if req.Win {
			targetProductID = msConfig.WinnerRewardProductID
			targetPoints = msConfig.WinnerRewardPoints
			if targetPoints == 0 && targetProductID == 0 {
				targetPoints = 100 // 兜底
			}
		} else {
			targetProductID = msConfig.ParticipationRewardProductID
			targetPoints = msConfig.ParticipationRewardPoints
			if targetPoints == 0 && targetProductID == 0 {
				targetPoints = 10 // 兜底
			}
		}

		// 3. 发放奖励
		if targetProductID > 0 {
			res, err := h.userSvc.GrantReward(ctx.RequestContext(), uid, usersvc.GrantRewardRequest{
				ProductID: targetProductID,
				Quantity:  1,
				Remark:    "扫雷游戏奖励",
			})
			if err != nil || !res.Success {
				h.logger.Error("Failed to grant game product reward", zap.Error(err), zap.String("msg", res.Message))
				rewardMsg = "奖励发放失败"
			} else {
				rewardMsg = "获得奖品"
			}
		} else if targetPoints > 0 {
			err := h.userSvc.AddPointsWithAction(ctx.RequestContext(), uid, targetPoints, "game_reward", "扫雷游戏奖励", "minesweeper_settle", nil, nil)
			if err != nil {
				h.logger.Error("Failed to grant game points", zap.Error(err))
			}
			rewardMsg = strconv.FormatInt(targetPoints, 10) + "积分"
		}

		ctx.Payload(&settleResponse{Success: true, Reward: rewardMsg})
	}
}

type consumeTicketRequest struct {
	UserID   string `json:"user_id"`
	GameCode string `json:"game_code"`
	Ticket   string `json:"ticket"`
}

type consumeTicketResponse struct {
	Success bool   `json:"success"`
	Error   string `json:"error,omitempty"`
}

// ConsumeTicket Internal扣减游戏次数（匹配成功后由Nakama调用）
// @Summary 扣减游戏次数
// @Tags Internal.游戏
// @Param RequestBody body consumeTicketRequest true "请求参数"
// @Success 200 {object} consumeTicketResponse
// @Router /internal/game/consume-ticket [post]
func (h *handler) ConsumeTicket() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(consumeTicketRequest)
		if err := ctx.ShouldBindJSON(req); err != nil {
			ctx.Payload(&consumeTicketResponse{Success: false, Error: "参数错误"})
			return
		}

		uid, _ := strconv.ParseInt(req.UserID, 10, 64)
		if uid <= 0 {
			ctx.Payload(&consumeTicketResponse{Success: false, Error: "无效的用户ID"})
			return
		}

		gameCode := req.GameCode
		if gameCode == "" {
			gameCode = "minesweeper"
		}

		// 扣减游戏次数
		err := h.ticketSvc.UseTicket(ctx.RequestContext(), uid, gameCode)
		if err != nil {
			h.logger.Error("Failed to consume ticket", zap.Int64("user_id", uid), zap.String("game_code", gameCode), zap.Error(err))
			ctx.Payload(&consumeTicketResponse{Success: false, Error: err.Error()})
			return
		}

		// 使 ticket 失效（防止重复扣减）
		if req.Ticket != "" {
			h.gameTokenSvc.InvalidateTicket(ctx.RequestContext(), req.Ticket)
		}

		h.logger.Info("Ticket consumed on match success", zap.Int64("user_id", uid), zap.String("game_code", gameCode))
		ctx.Payload(&consumeTicketResponse{Success: true})
	}
}

// GetMinesweeperConfig Internal获取扫雷配置
// @Summary 获取扫雷配置
// @Tags Internal.游戏
// @Success 200 {object} map[string]interface{}
// @Router /internal/game/minesweeper/config [get]
func (h *handler) GetMinesweeperConfig() core.HandlerFunc {
	return func(ctx core.Context) {
		configKey := "game_minesweeper_config"
		conf, err := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq(configKey)).First()
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, "获取配置失败"))
			return
		}

		var gameConfig map[string]interface{}
		if err := json.Unmarshal([]byte(conf.ConfigValue), &gameConfig); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, "解析配置失败"))
			return
		}

		ctx.Payload(gameConfig)
	}
}

// ========== Helpers ==========

func generateTicketToken(userID int64) string {
	return "GT" + randomString(16)
}

func randomString(n int) string {
	const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, n)
	for i := range b {
		b[i] = letters[i%len(letters)]
	}
	return string(b)
}
